Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions pkg/rulemanager/cel/libraries/applicationprofile/ap.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,28 @@ func (l *apLibrary) Declarations() map[string][]cel.FunctionOpt {
}),
),
},
// ap.was_path_opened_with_suffix and ap.was_path_opened_with_prefix
// — rule-author contract (CodeRabbit upstream PR #807 finding #7):
//
// These helpers answer "did any RECORDED concrete path open match
// this suffix/prefix?". When the profile-projection cache is in
// pass-through mode (no rule declared an Opens-projection slice,
// so cp.Opens.All == true), wildcard patterns in cp.Opens.Patterns
// are NOT scanned via string-level HasSuffix/HasPrefix because the
// pattern text contains '*' / '⋯' tokens whose string shape doesn't
// safely answer suffix/prefix questions (see open.go comment).
// Concrete-only Values are scanned.
//
// False-negative gap: if a profile entry is `/var/log/pods/*/foo.log`,
// the runtime path `/var/log/pods/web-7d6f/volumes/foo.log` actually
// matches this pattern, but `was_path_opened_with_suffix("/foo.log")`
// returns FALSE because the pattern text doesn't end in `/foo.log`
// literally. Rule authors who need wildcard-aware coverage should
// either: (a) declare an Opens projection slice in the rule's
// ProfileDataRequired (then SuffixHits/PrefixHits become authoritative
// and the projector pre-computes the hit map for wildcard entries),
// or (b) use ap.was_path_opened(path) which DOES run dynamic-segment
// matching over Patterns via CompareDynamic.
"ap.was_path_opened_with_suffix": {
cel.Overload(
"ap_was_path_opened_with_suffix", []*cel.Type{cel.StringType, cel.StringType}, cel.BoolType,
Expand Down
36 changes: 24 additions & 12 deletions pkg/rulemanager/cel/libraries/applicationprofile/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ func (l *apLibrary) wasPathOpened(containerID, path ref.Val) ref.Val {
return types.Bool(false)
}

// wasPathOpenedWithFlags answers whether the projected ApplicationProfile
// contains an open-entry whose path matches the given path. The flags
// argument is parsed and validated for shape but is not used for matching
// in v1 — the OpenFlagsByPath projection slice is out of scope for v1
// (composite-key projection would balloon the cache footprint). When the
// flags-projection slice is added in a future spec revision, this helper
// becomes the path-AND-flag matcher and v1 callers continue to work.
func (l *apLibrary) wasPathOpenedWithFlags(containerID, path, flags ref.Val) ref.Val {
if l.objectCache == nil {
return types.NewErr("objectCache is nil")
Expand Down Expand Up @@ -105,17 +112,21 @@ func (l *apLibrary) wasPathOpenedWithSuffix(containerID, suffix ref.Val) ref.Val
}

if cp.Opens.All {
// All entries retained — scan to check for the suffix.
// All entries retained (no rule declared SuffixHits-style
// projection). Scan ONLY concrete entries in Values — Patterns
// contain wildcard tokens ('*' / '⋯') whose text doesn't safely
// answer suffix questions. CodeRabbit PR #43 open.go:79: a
// retained Pattern like "/var/log/pods/*/volumes/..." doesn't
// end with the concrete suffix "foo.log", but the concrete open
// it stands in for might — strings.HasSuffix on the pattern
// text returns false and produces a false negative. Patterns
// are inherently wildcard-shaped; concrete-path semantics live
// in Values (and in SuffixHits when projection is active).
for openPath := range cp.Opens.Values {
if strings.HasSuffix(openPath, suffixStr) {
return types.Bool(true)
}
}
for _, openPath := range cp.Opens.Patterns {
if strings.HasSuffix(openPath, suffixStr) {
return types.Bool(true)
}
}
return types.Bool(false)
}
// Projection applied — SuffixHits is authoritative; absent key = undeclared.
Expand Down Expand Up @@ -149,17 +160,18 @@ func (l *apLibrary) wasPathOpenedWithPrefix(containerID, prefix ref.Val) ref.Val
}

if cp.Opens.All {
// All entries retained — scan to check for the prefix.
// All entries retained — scan ONLY Values (concrete paths).
// Patterns contain wildcard tokens whose text doesn't safely
// answer prefix questions; a pattern starting with "/var/⋯/log"
// matches concrete paths starting with "/var/anything/log" but
// strings.HasPrefix against the pattern text returns false for
// "/var/foo/log...". Same fix as wasPathOpenedWithSuffix above.
// CodeRabbit PR #43 open.go:79 (Also applies to 111-123).
for openPath := range cp.Opens.Values {
if strings.HasPrefix(openPath, prefixStr) {
return types.Bool(true)
}
}
for _, openPath := range cp.Opens.Patterns {
if strings.HasPrefix(openPath, prefixStr) {
return types.Bool(true)
}
}
return types.Bool(false)
}
// Projection applied — PrefixHits is authoritative; absent key = undeclared.
Expand Down