diff --git a/pkg/objectcache/containerprofilecache/projection_apply.go b/pkg/objectcache/containerprofilecache/projection_apply.go index c0d7e129f..711ac7311 100644 --- a/pkg/objectcache/containerprofilecache/projection_apply.go +++ b/pkg/objectcache/containerprofilecache/projection_apply.go @@ -143,10 +143,19 @@ func projectField(spec objectcache.FieldSpec, rawEntries []string, isPathSurface return pf } -// containsDynamicSegment reports whether e contains the dynamic-path marker. -// Always references the constant from the storage package; never hardcodes the glyph. +// containsDynamicSegment reports whether e contains a wildcard-path marker — +// either the one-segment DynamicIdentifier ("⋯") OR the zero-or-more +// WildcardIdentifier ("*"). On path surfaces both are dynamic and must be +// routed to Patterns, never treated as literal Values. Omitting "*" here +// silently misclassifies entries like "/etc/ssl/*" as literals; that happens +// to be harmless for was_path_opened (both Values and Patterns are matched via +// CompareDynamic), but it is wrong for any consumer that treats Values as exact +// membership and it drops "*"-only entries a rule needs when spec.All is false +// and no prefix/suffix matcher retains them. Always reference the storage +// constants; never hardcode the glyphs. func containsDynamicSegment(e string) bool { - return strings.Contains(e, dynamicpathdetector.DynamicIdentifier) + return strings.Contains(e, dynamicpathdetector.DynamicIdentifier) || + strings.Contains(e, dynamicpathdetector.WildcardIdentifier) } // --- Field extractors --- diff --git a/pkg/objectcache/containerprofilecache/projection_wildcard_classification_test.go b/pkg/objectcache/containerprofilecache/projection_wildcard_classification_test.go new file mode 100644 index 000000000..b3d96f3d1 --- /dev/null +++ b/pkg/objectcache/containerprofilecache/projection_wildcard_classification_test.go @@ -0,0 +1,29 @@ +package containerprofilecache + +import ( + "testing" + + "github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestProjectField_StarPathRoutesToPatterns pins that a path-surface opens +// entry containing the "*" WildcardIdentifier is classified as a Pattern, +// not a literal Value. Regression guard: containsDynamicSegment previously +// recognised only "⋯", silently routing "/etc/ssl/*" into Values. +func TestProjectField_StarPathRoutesToPatterns(t *testing.T) { + cp := &v1beta1.ContainerProfile{ + Spec: v1beta1.ContainerProfileSpec{ + Opens: []v1beta1.OpenCalls{{Path: "/etc/ssl/*"}, {Path: "/etc/ld.so.cache"}}, + }, + } + pcp := Apply(nil, cp, nil) // nil spec => pass-through (All=true) + + require.Contains(t, pcp.Opens.Patterns, "/etc/ssl/*", + "a '*'-bearing path entry must be a Pattern") + _, inValues := pcp.Opens.Values["/etc/ssl/*"] + assert.False(t, inValues, "'*'-bearing path entry must NOT be a literal Value") + _, cacheInValues := pcp.Opens.Values["/etc/ld.so.cache"] + assert.True(t, cacheInValues, "a literal path entry stays a Value") +}