diff --git a/pkg/security/secl/model/model.go b/pkg/security/secl/model/model.go index b7a7a7df50cc4..9e3ba2db7b5ba 100644 --- a/pkg/security/secl/model/model.go +++ b/pkg/security/secl/model/model.go @@ -230,6 +230,13 @@ func (e *Event) AddToFlags(flag uint32) { e.Flags |= flag } +// ResetAnomalyDetectionEvent removes the anomaly detection event flag +func (e *Event) ResetAnomalyDetectionEvent() { + if e.IsAnomalyDetectionEvent() { + e.RemoveFromFlags(EventFlagsAnomalyDetectionEvent) + } +} + // RemoveFromFlags remove a flag to the event func (e *Event) RemoveFromFlags(flag uint32) { e.Flags ^= flag diff --git a/pkg/security/seclwin/model/model.go b/pkg/security/seclwin/model/model.go index b7a7a7df50cc4..9e3ba2db7b5ba 100644 --- a/pkg/security/seclwin/model/model.go +++ b/pkg/security/seclwin/model/model.go @@ -230,6 +230,13 @@ func (e *Event) AddToFlags(flag uint32) { e.Flags |= flag } +// ResetAnomalyDetectionEvent removes the anomaly detection event flag +func (e *Event) ResetAnomalyDetectionEvent() { + if e.IsAnomalyDetectionEvent() { + e.RemoveFromFlags(EventFlagsAnomalyDetectionEvent) + } +} + // RemoveFromFlags remove a flag to the event func (e *Event) RemoveFromFlags(flag uint32) { e.Flags ^= flag diff --git a/pkg/security/security_profile/activity_tree/activity_tree.go b/pkg/security/security_profile/activity_tree/activity_tree.go index 9a1110dd5882e..7987e8061f753 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree.go +++ b/pkg/security/security_profile/activity_tree/activity_tree.go @@ -401,7 +401,7 @@ func (at *ActivityTree) insertEvent(event *model.Event, dryRun bool, insertMissi case model.BindEventType: return node.InsertBindEvent(event, imageTag, generationType, at.Stats, dryRun), nil case model.SyscallsEventType: - return node.InsertSyscalls(event, at.SyscallsMask), nil + return node.InsertSyscalls(event, imageTag, at.SyscallsMask, at.Stats, dryRun), nil case model.ExitEventType: // Update the exit time of the process (this is purely informative, do not rely on timestamps to detect // execed children) @@ -936,7 +936,7 @@ func (at *ActivityTree) ExtractSyscalls(arch string) []string { at.visit(func(processNode *ProcessNode) { for _, s := range processNode.Syscalls { - sycallKey := utils.SyscallKey{Arch: arch, ID: s} + sycallKey := utils.SyscallKey{Arch: arch, ID: s.Syscall} syscall, ok := utils.Syscalls[sycallKey] if ok { syscalls = append(syscalls, syscall) diff --git a/pkg/security/security_profile/activity_tree/activity_tree_graph.go b/pkg/security/security_profile/activity_tree/activity_tree_graph.go index 4ac254f015276..14f70a5a98ddd 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_graph.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_graph.go @@ -293,7 +293,7 @@ func (at *ActivityTree) prepareFileNode(f *FileNode, data *utils.Graph, prefix s func (at *ActivityTree) prepareSyscallsNode(p *ProcessNode, data *utils.Graph) utils.GraphID { label := "<" for _, s := range p.Syscalls { - label += "" + label += "" } label += "
" + model.Syscall(s).String() + "
" + model.Syscall(s.Syscall).String() + "
>" diff --git a/pkg/security/security_profile/activity_tree/activity_tree_proto_dec_v1.go b/pkg/security/security_profile/activity_tree/activity_tree_proto_dec_v1.go index 95671149ad057..09ad5a25a4c9a 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_proto_dec_v1.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_proto_dec_v1.go @@ -39,7 +39,7 @@ func protoDecodeProcessActivityNode(parent ProcessNodeParent, pan *adproto.Proce DNSNames: make(map[string]*DNSNode, len(pan.DnsNames)), IMDSEvents: make(map[model.IMDSEvent]*IMDSNode, len(pan.ImdsEvents)), Sockets: make([]*SocketNode, 0, len(pan.Sockets)), - Syscalls: make([]int, 0, len(pan.Syscalls)), + Syscalls: make([]*SyscallNode, 0, len(pan.Syscalls)), ImageTags: pan.ImageTags, } @@ -74,7 +74,7 @@ func protoDecodeProcessActivityNode(parent ProcessNodeParent, pan *adproto.Proce } for _, sysc := range pan.Syscalls { - ppan.Syscalls = append(ppan.Syscalls, int(sysc)) + ppan.Syscalls = append(ppan.Syscalls, NewSyscallNode(int(sysc), "", Unknown)) } return ppan diff --git a/pkg/security/security_profile/activity_tree/activity_tree_proto_enc_v1.go b/pkg/security/security_profile/activity_tree/activity_tree_proto_enc_v1.go index b00ec1a6c0055..30b0b46e29382 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_proto_enc_v1.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_proto_enc_v1.go @@ -71,7 +71,7 @@ func processActivityNodeToProto(pan *ProcessNode) *adproto.ProcessActivityNode { } for _, sysc := range pan.Syscalls { - ppan.Syscalls = append(ppan.Syscalls, uint32(sysc)) + ppan.Syscalls = append(ppan.Syscalls, uint32(sysc.Syscall)) } return ppan diff --git a/pkg/security/security_profile/activity_tree/activity_tree_stats.go b/pkg/security/security_profile/activity_tree/activity_tree_stats.go index 0ee7dbbc1b82f..7f10be52327b4 100644 --- a/pkg/security/security_profile/activity_tree/activity_tree_stats.go +++ b/pkg/security/security_profile/activity_tree/activity_tree_stats.go @@ -26,6 +26,7 @@ type Stats struct { DNSNodes int64 SocketNodes int64 IMDSNodes int64 + SyscallNodes int64 counts map[model.EventType]*statsPerEventType } @@ -72,6 +73,7 @@ func (stats *Stats) ApproximateSize() int64 { total += stats.DNSNodes * int64(unsafe.Sizeof(DNSNode{})) // 24 total += stats.SocketNodes * int64(unsafe.Sizeof(SocketNode{})) // 40 total += stats.IMDSNodes * int64(unsafe.Sizeof(IMDSNode{})) + total += stats.SyscallNodes * int64(unsafe.Sizeof(SyscallNode{})) return total } diff --git a/pkg/security/security_profile/activity_tree/process_node.go b/pkg/security/security_profile/activity_tree/process_node.go index a51951c3a7dfd..11525c9eacc79 100644 --- a/pkg/security/security_profile/activity_tree/process_node.go +++ b/pkg/security/security_profile/activity_tree/process_node.go @@ -43,7 +43,7 @@ type ProcessNode struct { IMDSEvents map[model.IMDSEvent]*IMDSNode Sockets []*SocketNode - Syscalls []int + Syscalls []*SyscallNode Children []*ProcessNode } @@ -200,20 +200,29 @@ func (pn *ProcessNode) Matches(entry *model.Process, matchArgs bool, normalize b } // InsertSyscalls inserts the syscall of the process in the dump -func (pn *ProcessNode) InsertSyscalls(e *model.Event, syscallMask map[int]int) bool { +func (pn *ProcessNode) InsertSyscalls(e *model.Event, imageTag string, syscallMask map[int]int, stats *Stats, dryRun bool) bool { var hasNewSyscalls bool newSyscallLoop: for _, newSyscall := range e.Syscalls.Syscalls { for _, existingSyscall := range pn.Syscalls { - if existingSyscall == int(newSyscall) { + if existingSyscall.Syscall == int(newSyscall) { + if imageTag != "" && !slices.Contains(existingSyscall.ImageTags, imageTag) { + existingSyscall.ImageTags = append(existingSyscall.ImageTags, imageTag) + } continue newSyscallLoop } } - pn.Syscalls = append(pn.Syscalls, int(newSyscall)) - syscallMask[int(newSyscall)] = int(newSyscall) hasNewSyscalls = true + if dryRun { + // exit early + break + } + pn.Syscalls = append(pn.Syscalls, NewSyscallNode(int(newSyscall), imageTag, Runtime)) + syscallMask[int(newSyscall)] = int(newSyscall) + stats.SyscallNodes++ } + return hasNewSyscalls } @@ -389,6 +398,9 @@ func (pn *ProcessNode) TagAllNodes(imageTag string) { for _, sock := range pn.Sockets { sock.appendImageTag(imageTag) } + for _, scall := range pn.Syscalls { + scall.appendImageTag(imageTag) + } for _, child := range pn.Children { child.TagAllNodes(imageTag) } @@ -449,6 +461,15 @@ func (pn *ProcessNode) EvictImageTag(imageTag string, DNSNames *utils.StringKeys } pn.Sockets = newSockets + newSyscalls := []*SyscallNode{} + for _, scall := range pn.Syscalls { + if shouldRemove := scall.evictImageTag(imageTag); !shouldRemove { + newSyscalls = append(newSyscalls, scall) + SyscallsMask[scall.Syscall] = scall.Syscall + } + } + pn.Syscalls = newSyscalls + newChildren := []*ProcessNode{} for _, child := range pn.Children { if shouldRemoveNode := child.EvictImageTag(imageTag, DNSNames, SyscallsMask); !shouldRemoveNode { @@ -456,9 +477,5 @@ func (pn *ProcessNode) EvictImageTag(imageTag string, DNSNames *utils.StringKeys } } pn.Children = newChildren - - for _, id := range pn.Syscalls { - SyscallsMask[id] = id - } return false } diff --git a/pkg/security/security_profile/activity_tree/syscalls_node.go b/pkg/security/security_profile/activity_tree/syscalls_node.go new file mode 100644 index 0000000000000..3ccd457796253 --- /dev/null +++ b/pkg/security/security_profile/activity_tree/syscalls_node.go @@ -0,0 +1,46 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build linux + +// Package activitytree holds activitytree related files +package activitytree + +// SyscallNode is used to store a syscall node +type SyscallNode struct { + ImageTags []string + GenerationType NodeGenerationType + + Syscall int +} + +func (sn *SyscallNode) appendImageTag(imageTag string) { + sn.ImageTags, _ = AppendIfNotPresent(sn.ImageTags, imageTag) +} + +func (sn *SyscallNode) evictImageTag(imageTag string) bool { + imageTags, removed := removeImageTagFromList(sn.ImageTags, imageTag) + if !removed { + return false + } + if len(imageTags) == 0 { + return true + } + sn.ImageTags = imageTags + return false +} + +// NewSyscallNode returns a new SyscallNode instance +func NewSyscallNode(syscall int, imageTag string, generationType NodeGenerationType) *SyscallNode { + var imageTags []string + if len(imageTag) != 0 { + imageTags = append(imageTags, imageTag) + } + return &SyscallNode{ + Syscall: syscall, + GenerationType: generationType, + ImageTags: imageTags, + } +} diff --git a/pkg/security/security_profile/profile/manager.go b/pkg/security/security_profile/profile/manager.go index 8f8797ba3bd23..aadd9b94cd88b 100644 --- a/pkg/security/security_profile/profile/manager.go +++ b/pkg/security/security_profile/profile/manager.go @@ -767,6 +767,10 @@ func (m *SecurityProfileManager) LookupEventInProfiles(event *model.Event) { globalEventTypeProfilState := profile.GetGlobalEventTypeState(event.GetEventType()) if globalEventTypeProfilState == model.UnstableEventType { m.incrementEventFilteringStat(event.GetEventType(), model.UnstableEventType, NA) + // The anomaly flag can be set in kernel space by our eBPF programs (currently applies only to syscalls), reset + // the anomaly flag if the user space profile considers it to not be an anomaly. Here, when a version is unstable, + // we don't want to generate anomalies for this profile anymore. + event.ResetAnomalyDetectionEvent() return } @@ -778,6 +782,13 @@ func (m *SecurityProfileManager) LookupEventInProfiles(event *model.Event) { case model.NoProfile, model.ProfileAtMaxSize, model.UnstableEventType: // an error occurred or we are in unstable state // do not link the profile to avoid sending anomalies + + // The anomaly flag can be set in kernel space by our eBPF programs (currently applies only to syscalls), reset + // the anomaly flag if the user space profile considers it to not be an anomaly. + // We can also get a syscall anomaly detection kernel space for runc, which is ignored in the activity tree + // (i.e. tryAutolearn returns NoProfile) because "runc" can't be a root node. + event.ResetAnomalyDetectionEvent() + return case model.AutoLearning, model.WorkloadWarmup: // the event was either already in the profile, or has just been inserted @@ -798,12 +809,20 @@ func (m *SecurityProfileManager) LookupEventInProfiles(event *model.Event) { if err != nil { // ignore, evaluation failed m.incrementEventFilteringStat(event.GetEventType(), model.NoProfile, NA) + + // The anomaly flag can be set in kernel space by our eBPF programs (currently applies only to syscalls), reset + // the anomaly flag if the user space profile considers it to not be an anomaly. + event.ResetAnomalyDetectionEvent() return } FillProfileContextFromProfile(&event.SecurityProfileContext, profile, imageTag, profileState) if found { event.AddToFlags(model.EventFlagsSecurityProfileInProfile) m.incrementEventFilteringStat(event.GetEventType(), profileState, InProfile) + + // The anomaly flag can be set in kernel space by our eBPF programs (currently applies only to syscalls), reset + // the anomaly flag if the user space profile considers it to not be an anomaly. + event.ResetAnomalyDetectionEvent() } else { m.incrementEventFilteringStat(event.GetEventType(), profileState, NotInProfile) if m.canGenerateAnomaliesFor(event) { @@ -851,11 +870,19 @@ func (m *SecurityProfileManager) tryAutolearn(profile *SecurityProfile, ctx *Ver globalEventTypeState := profile.GetGlobalEventTypeState(event.GetEventType()) if globalEventTypeState == model.StableEventType && m.canGenerateAnomaliesFor(event) { event.AddToFlags(model.EventFlagsAnomalyDetectionEvent) + } else { + // The anomaly flag can be set in kernel space by our eBPF programs (currently applies only to syscalls), reset + // the anomaly flag if the user space profile considers it to not be an anomaly: there is a new entry and no + // previous version is in stable state. + event.ResetAnomalyDetectionEvent() } m.incrementEventFilteringStat(event.GetEventType(), profileState, NotInProfile) } else { // no newEntry m.incrementEventFilteringStat(event.GetEventType(), profileState, InProfile) + // The anomaly flag can be set in kernel space by our eBPF programs (currently applies only to syscalls), reset + // the anomaly flag if the user space profile considers it to not be an anomaly + event.ResetAnomalyDetectionEvent() } return profileState } diff --git a/pkg/security/tests/activity_dumps_test.go b/pkg/security/tests/activity_dumps_test.go index c64e05a3b76ca..0a1bd7a530055 100644 --- a/pkg/security/tests/activity_dumps_test.go +++ b/pkg/security/tests/activity_dumps_test.go @@ -351,10 +351,10 @@ func TestActivityDumps(t *testing.T) { var exitOK, bindOK bool for _, node := range nodes { for _, s := range node.Syscalls { - if s == int(model.SysExit) || s == int(model.SysExitGroup) { + if s.Syscall == int(model.SysExit) || s.Syscall == int(model.SysExitGroup) { exitOK = true } - if s == int(model.SysBind) { + if s.Syscall == int(model.SysBind) { bindOK = true } } diff --git a/pkg/security/tests/security_profile_test.go b/pkg/security/tests/security_profile_test.go index ad87dc7e0560c..2936854c55758 100644 --- a/pkg/security/tests/security_profile_test.go +++ b/pkg/security/tests/security_profile_test.go @@ -10,6 +10,7 @@ package tests import ( "errors" + "fmt" "os" "slices" "strings" @@ -1372,7 +1373,196 @@ func TestSecurityProfileLifeCycleDNS(t *testing.T) { }) } -func TestSecurityProfileLifeCycleEvictitonProcess(t *testing.T) { +func TestSecurityProfileLifeCycleSyscall(t *testing.T) { + SkipIfNotAvailable(t) + + // skip test that are about to be run on docker (to avoid trying spawning docker in docker) + if testEnvironment == DockerEnvironment { + t.Skip("Skip test spawning docker containers on docker") + } + if _, err := whichNonFatal("docker"); err != nil { + t.Skip("Skip test where docker is unavailable") + } + if !IsDedicatedNodeForAD() { + t.Skip("Skip test when not run in dedicated env") + } + + var expectedFormats = []string{"profile"} + var testActivityDumpTracedEventTypes = []string{"exec", "syscalls"} + + outputDir := t.TempDir() + os.MkdirAll(outputDir, 0755) + defer os.RemoveAll(outputDir) + + fakeManualResolver := NewFakeManualResolver() + + test, err := newTestModule(t, nil, []*rules.RuleDefinition{}, withStaticOpts(testOpts{ + enableActivityDump: true, + activityDumpRateLimiter: 200, + activityDumpTracedCgroupsCount: 10, + activityDumpDuration: testActivityDumpDuration, + activityDumpLocalStorageDirectory: outputDir, + activityDumpLocalStorageCompression: false, + activityDumpLocalStorageFormats: expectedFormats, + activityDumpTracedEventTypes: testActivityDumpTracedEventTypes, + enableSecurityProfile: true, + securityProfileDir: outputDir, + securityProfileWatchDir: true, + enableAnomalyDetection: true, + anomalyDetectionEventTypes: testActivityDumpTracedEventTypes, + anomalyDetectionMinimumStablePeriodExec: 10 * time.Second, + anomalyDetectionMinimumStablePeriodDNS: 10 * time.Second, + anomalyDetectionDefaultMinimumStablePeriod: 10 * time.Second, + anomalyDetectionWarmupPeriod: 1 * time.Second, + tagsResolver: fakeManualResolver, + })) + if err != nil { + t.Fatal(err) + } + defer test.Close() + syscallTester, err := loadSyscallTester(t, test, "syscall_tester") + if err != nil { + t.Fatal(err) + } + + dockerInstanceV1, err := test.StartADocker() + if err != nil { + t.Fatal(err) + } + defer dockerInstanceV1.stop() + + cmd := dockerInstanceV1.Command(syscallTester, []string{"sleep", "1"}, []string{}) + _, err = cmd.CombinedOutput() + if err != nil { + t.Fatal(err) + } + time.Sleep(1 * time.Second) // a quick sleep to let events be added to the dump + + err = test.StopActivityDump("", dockerInstanceV1.containerID) + if err != nil { + t.Fatal(err) + } + time.Sleep(6 * time.Second) // a quick sleep to let the profile be loaded (5sec debounce + 1sec spare) + + // HERE: V1 is learning + + // Some syscall will be missing from the initial dump because they had no way to come back to user space + // (i.e. no new syscall to flush the dirty entry + no new exec + no new exit) + t.Run("life-cycle-v1-learning", func(t *testing.T) { + err = test.GetCustomEventSent(t, func() error { + cmd := dockerInstanceV1.Command("sleep", []string{"1"}, []string{}) + _, err = cmd.CombinedOutput() + return err + }, func(r *rules.Rule, event *events.CustomEvent) bool { + // We shouldn't see anything: the profile is still learning + data, _ := event.MarshalJSON() + t.Fatal(fmt.Errorf("syscall anomaly detected when it should have been ignored: %s", string(data))) + // we answer false on purpose: we might have 2 or more syscall anomaly events + return false + }, time.Second*2, model.SyscallsEventType, events.AnomalyDetectionRuleID) + }) + + time.Sleep(time.Second * 10) // waiting for the stable period + + // HERE: V1 is stable + + t.Run("life-cycle-v1-stable-no-anomaly", func(t *testing.T) { + err = test.GetCustomEventSent(t, func() error { + cmd := dockerInstanceV1.Command("sleep", []string{"1"}, []string{}) + _, err = cmd.CombinedOutput() + return err + }, func(r *rules.Rule, event *events.CustomEvent) bool { + // this time we shouldn't see anything new. + data, _ := event.MarshalJSON() + t.Fatal(fmt.Errorf("syscall anomaly detected when it should have been ignored: %s", string(data))) + return false + }, time.Second*2, model.SyscallsEventType, events.AnomalyDetectionRuleID) + }) + + t.Run("life-cycle-v1-stable-anomaly", func(t *testing.T) { + err = test.GetCustomEventSent(t, func() error { + // this will generate new syscalls, and should therefore generate an anomaly + cmd := dockerInstanceV1.Command("nslookup", []string{"google.com"}, []string{}) + _, _ = cmd.CombinedOutput() + return nil + }, func(r *rules.Rule, event *events.CustomEvent) bool { + assert.Equal(t, events.AnomalyDetectionRuleID, r.Rule.ID, "wrong custom event rule ID") + return true + }, time.Second*3, model.SyscallsEventType, events.AnomalyDetectionRuleID) + if err != nil { + t.Fatal(err) + } + }) + + selector := fakeManualResolver.GetContainerSelector(dockerInstanceV1.containerID) + fakeManualResolver.SpecifyNextSelector(&cgroupModel.WorkloadSelector{ + Image: selector.Image, + Tag: selector.Tag + "+", + }) + dockerInstanceV2, err := test.StartADocker() + if err != nil { + t.Fatal(err) + } + defer dockerInstanceV2.stop() + + // HERE: V1 is stable and V2 is learning + + t.Run("life-cycle-v1-stable-v2-learning-anomaly", func(t *testing.T) { + var gotSyscallsEvent bool + err = test.GetCustomEventSent(t, func() error { + cmd := dockerInstanceV2.Command("date", []string{}, []string{}) + _, _ = cmd.CombinedOutput() + return nil + }, func(r *rules.Rule, event *events.CustomEvent) bool { + // we should see an anomaly that will be inserted in the profile + assert.Equal(t, events.AnomalyDetectionRuleID, r.Rule.ID, "wrong custom event rule ID") + gotSyscallsEvent = true + // there may be multiple syscalls events + return false + }, time.Second*3, model.SyscallsEventType, events.AnomalyDetectionRuleID) + if !gotSyscallsEvent { + t.Fatal(err) + } + }) + + t.Run("life-cycle-v1-stable-v2-learning-no-anomaly", func(t *testing.T) { + err = test.GetCustomEventSent(t, func() error { + cmd := dockerInstanceV2.Command("date", []string{}, []string{}) + _, _ = cmd.CombinedOutput() + return nil + }, func(r *rules.Rule, event *events.CustomEvent) bool { + // this time we shouldn't see anything new. + data, _ := event.MarshalJSON() + t.Fatal(fmt.Errorf("syscall anomaly detected when it should have been ignored: %s", string(data))) + return false + }, time.Second*2, model.SyscallsEventType, events.AnomalyDetectionRuleID) + }) + + if err := test.SetProfileVersionState(&cgroupModel.WorkloadSelector{ + Image: selector.Image, + Tag: "*", + }, selector.Tag, model.UnstableEventType); err != nil { + t.Fatal(err) + } + + // HERE: V1 is unstable and V2 is learning + + t.Run("life-cycle-v1-unstable-v2-learning", func(t *testing.T) { + err = test.GetCustomEventSent(t, func() error { + cmd := dockerInstanceV1.Command("nslookup", []string{"google.com"}, []string{}) + _, _ = cmd.CombinedOutput() + return nil + }, func(r *rules.Rule, event *events.CustomEvent) bool { + // We shouldn't see anything: the profile is unstable + data, _ := event.MarshalJSON() + t.Fatal(fmt.Errorf("syscall anomaly detected when it should have been ignored: %s", string(data))) + // we answer false on purpose: we might have 2 or more syscall anomaly events + return false + }, time.Second*2, model.SyscallsEventType, events.AnomalyDetectionRuleID) + }) +} + +func TestSecurityProfileLifeCycleEvictionProcess(t *testing.T) { SkipIfNotAvailable(t) // skip test that are about to be run on docker (to avoid trying spawning docker in docker) @@ -1550,7 +1740,7 @@ func TestSecurityProfileLifeCycleEvictitonProcess(t *testing.T) { }) } -func TestSecurityProfileLifeCycleEvictitonDNS(t *testing.T) { +func TestSecurityProfileLifeCycleEvictionDNS(t *testing.T) { SkipIfNotAvailable(t) // skip test that are about to be run on docker (to avoid trying spawning docker in docker) @@ -1728,7 +1918,7 @@ func TestSecurityProfileLifeCycleEvictitonDNS(t *testing.T) { }) } -func TestSecurityProfileLifeCycleEvictitonProcessUnstable(t *testing.T) { +func TestSecurityProfileLifeCycleEvictionProcessUnstable(t *testing.T) { SkipIfNotAvailable(t) // skip test that are about to be run on docker (to avoid trying spawning docker in docker)