Skip to content

feat(cel): was_path_opened* CEL helpers in applicationprofile library#811

Open
entlein wants to merge 4 commits into
kubescape:mainfrom
k8sstormcenter:upstream-pr/opens-na-cel-helpers
Open

feat(cel): was_path_opened* CEL helpers in applicationprofile library#811
entlein wants to merge 4 commits into
kubescape:mainfrom
k8sstormcenter:upstream-pr/opens-na-cel-helpers

Conversation

@entlein
Copy link
Copy Markdown

@entlein entlein commented May 16, 2026

Layer 1 (path opens) — adds the four was_path_opened* CEL helpers to the applicationprofile library so rule authors can express open-event matching against the projected profile.

CEL surface added

Helper Args Returns Use
ap.was_path_opened(containerID, path) (string, string) bool path was opened during learning
ap.was_path_opened_with_flags(containerID, path, flags) (string, string, []string) bool path was opened AND any of flags were seen
ap.was_path_opened_with_suffix(containerID, suffix) (string, string) bool any opened path ends with suffix
ap.was_path_opened_with_prefix(containerID, prefix) (string, string) bool any opened path starts with prefix

All four route through pkg/rulemanager/cel/libraries/applicationprofile/open.go. The flags variant accepts a []string so rules can scope to e.g. ["O_RDWR", "O_CREAT"]. Cost-estimator entries are added in ap.go alongside the existing helper costs.

Dependencies

None on the node-agent side. Layer 1 — Tier 0 in the dependency advisory CodeRabbit posted on kubescape/storage#323. Companion to storage's analyze_opens.go anchored-* bugfix.

Tests

Unit coverage for each helper in open_test.go. Suite-level integration tests in integration_test.go exercising one open-event call per helper, including the was_path_opened_with_flags test case that demonstrates flag-list parsing.

Summary by CodeRabbit

  • Bug Fixes

    • Improved path pattern matching accuracy for application profiles, addressing false-negative gaps in wildcard pattern evaluation for suffix and prefix checks.
  • Documentation

    • Enhanced documentation clarifying path matching behavior and wildcard segment handling.
  • Tests

    • Added comprehensive unit and benchmark tests for path pattern matching functionality.

Review Change Stack

Signed-off-by: entlein <einentlein@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 16, 2026

Warning

Review limit reached

@entlein, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 59 minutes and 56 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9225ca97-ead1-4397-9629-c15366d0fb4a

📥 Commits

Reviewing files that changed from the base of the PR and between e30da5c and 654dedd.

📒 Files selected for processing (1)
  • tests/component_test.go
📝 Walkthrough

Walkthrough

The PR refines path-pattern matching in the applicationprofile CEL library to correctly evaluate suffix/prefix patterns containing wildcard segments. Two new helper functions extract concrete literal portions from dynamic patterns; matching becomes permissive when patterns end or start with wildcards. Unit tests, benchmarks, and Kubernetes deployments validate the new behavior.

Changes

Wildcard-aware path-pattern matching

Layer / File(s) Summary
Pattern matching helpers and core logic updates
pkg/rulemanager/cel/libraries/applicationprofile/open.go
Introduces patternConcreteSuffix and patternConcretePrefix to scan path-pattern segments and detect wildcards, extracting literal suffixes/prefixes for string matching. Updates wasPathOpenedWithSuffix and wasPathOpenedWithPrefix to use concrete tails/heads instead of raw patterns, returning true for empty computed tails/heads. Adds clarification that flags are parsed but unused in v1 matching.
Rule-author documentation for suffix/prefix behavior
pkg/rulemanager/cel/libraries/applicationprofile/ap.go
Inline comment block documenting the contract for ap.was_path_opened_with_suffix and ap.was_path_opened_with_prefix, including pass-through mode, wildcard limitations, and usage guidance.
Unit tests for pattern-matching contract
pkg/rulemanager/cel/libraries/applicationprofile/open_test.go
New tests verify concrete-tail/concrete-head scanning, permissive wildcard-tail/wildcard-head behavior, and helper function logic via mock object cache. Refactors existing CEL-evaluation tests to remove flag parameterization, aligning to path-only ap.was_path_opened evaluation.
Performance and allocation benchmarks
pkg/rulemanager/cel/libraries/applicationprofile/open_bench_test.go
Benchmarks measure allocation and performance: two "all mode" benchmarks for wasPathOpenedWithSuffix/wasPathOpenedWithPrefix across values-only, concrete-pattern, and wildcard-pattern shapes; one helper benchmark for patternConcreteSuffix.
Kubernetes test deployment manifests
tests/resources/curl-user-profile-wildcards-deployment.yaml, tests/resources/nginx-user-profile-deployment.yaml
New Deployment manifests for integration testing: curl container tagged for wildcard profile testing, nginx container tagged for regex profile testing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • kubescape/node-agent#799: Prior PR introduces projection-aware logic to Opens matching in the same applicationprofile/open.go file; this PR refines wildcard pattern evaluation within that projection-aware framework.

Suggested reviewers

  • slashben

Poem

🐰 Wildcards dance through patterns now,
Concrete tails extracted—take a bow!
Suffix, prefix, no false negatives today,
Path matching blooms in the CEL way! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding CEL helpers for path-opened operations to the applicationprofile library.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@entlein
Copy link
Copy Markdown
Author

entlein commented May 16, 2026

@coderabbitai review

Collective re-review request after applying your atomic-split advisory + rabbit-feedback fixes across the full SBOB upstream PR set. Each PR is now scoped to a single tier per your dependency map:

Tier 0 (opens):

Tier 2 (execs):

Tier 3 (network):

Tier 4 (tamper/sign):

Umbrellas:

Please confirm atomicity per your earlier advisory and flag any remaining cross-PR coupling we missed.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 16, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch PR `#811` details
gh pr view 811 --repo kubescape/node-agent --json title,body,files,additions,deletions,changedFiles

# List changed files
gh pr view 811 --repo kubescape/node-agent --json files --jq '.files[].path'

Length of output: 1979


🏁 Script executed:

#!/bin/bash
# Get the full diff for PR `#811`
gh pr diff 811 --repo kubescape/node-agent

echo "===FILE: ap.go==="
cat pkg/rulemanager/cel/libraries/applicationprofile/ap.go

echo "===FILE: open.go==="
cat pkg/rulemanager/cel/libraries/applicationprofile/open.go

Length of output: 29788


@entlein: ✅ Atomicity confirmed — PR #811 is properly scoped. Two files changed, single responsibility (the four was_path_opened* CEL helpers), zero coupling to Tier 2 (execs), Tier 3 (network), or Tier 4 (tamper/sign) PRs.

One soft compile-time coupling to flag: kubescape/storage#323 must be consistent on cp.Opens.{Values, Patterns, All, SuffixHits, PrefixHits} field shapes before this builds. That's expected for Tier 0 and correctly documented in the PR description.


Triggering a full review now. Findings follow below.


Review findings

1. wasPathOpenedWithFlags silently drops the flag constraint with no observable signal (open.go, Lines 58–89)

The v1 degradation (flags parsed, result discarded, falls through to path-only matching) is well-commented in the Go doc, but no metric is emitted for the degradation path — unlike wasPathOpenedWithSuffix/wasPathOpenedWithPrefix which call l.metrics.IncProjectionUndeclaredLiteral(...). In production, a rule author writing:

ap.was_path_opened_with_flags(containerID, "/etc/passwd", ["O_WRONLY"])

…gets a true on any open of /etc/passwd regardless of flags, with no way to observe that the flag constraint was silently dropped.

Suggestion — emit a degradation metric after the flags parse succeeds:

if _, err := celparse.ParseList[string](flags); err != nil {
    return types.NewErr("failed to parse flags: %v", err)
}
// v1: flag-projection not yet implemented; degrade to path-only matching.
if l.detailedMetrics && l.metrics != nil {
    l.metrics.IncProjectionUndeclaredLiteral("ap.was_path_opened_with_flags")
}

Or introduce a dedicated IncFlagsDegradation counter if you want to distinguish this from the projection-undeclared case.


2. Cost estimate for ap.was_path_opened_with_flags overstates v1 actual cost (ap.go, apCostEstimator)

The declared cost is 40 (comment: O(f*p) flag comparison), but in v1 the function performs the identical operations as ap.was_path_opened (cost 25) — no flag comparison happens. This penalises CEL policies using the flags variant until flag projection lands. Consider either:

  • Setting it to 25 (matching v1 reality) and bumping to 40 when the projection slice is added, or
  • Documenting the intentional forward-looking over-estimate in the comment.

3. wasPathOpened / wasPathOpenedWithFlags have no projection fast-path (open.go, Lines 14–89)

The suffix/prefix helpers check cp.Opens.All and use SuffixHits/PrefixHits when projection is active. The path-exact helpers always do a linear scan of both Values and Patterns, even when cp.Opens.All == false. This is presumably by design (no ExactHits map in the projection), but a brief comment mirroring the suffix/prefix block would make the intent clear for future maintainers:

// Note: unlike suffix/prefix, there is no ExactHits projection slice;
// path-exact matching always scans Values + Patterns regardless of cp.Opens.All.

4. Flag-string content not validated against known O_* constants (open.go, Line 71)

celparse.ParseList[string](flags) validates that the argument is a []string, but flag strings like "", "INVALID", or "o_rdwr" (lowercase) are accepted silently. Since the flags list is currently a no-op, this is a v1 carry-forward risk: when flag-projection lands in a future spec revision, existing rules with malformed flag strings will silently miss matches. Consider at least validating non-empty strings, or deferring with a TODO comment anchored to the storage field introduction ticket.


Cross-PR coupling matrix

This PR → Coupling type Safe to merge independently?
kubescape/storage#323 Soft compile-time (field shapes on cp.Opens) ✅ Yes, provided field names are stable
kubescape/node-agent#805 (exec) None
kubescape/node-agent#807 (R0040) None
kubescape/node-agent#812 (ExecsByPath) None
kubescape/node-agent#806 (network CEL) None
kubescape/node-agent#808/809 (tamper/sign) None

Summary: PR #811 is atomic and correctly placed at Tier 0. The three substantive items above (silent flag degradation, cost overestimate, and future flag validation) are all containable within this PR without touching any other tier.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

// 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 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: projectField() still keeps dynamic open paths only in cp.Opens.Patterns when cp.Opens.All == true (see TestApply_DynamicRetainedInPassThrough). Removing the Patterns scan here makes ap.was_path_opened_with_suffix() / _with_prefix() return false for dynamic entries in pass-through mode, so rules that omit profileDataRequired.opens regress. If the old strings.HasPrefix/HasSuffix behavior on pattern text is too permissive, we still need a narrower fallback here instead of ignoring Patterns entirely.

Entlein and others added 2 commits May 27, 2026 20:10
Removing the Patterns scan when cp.Opens.All==true (PR kubescape#811 original
diff) regressed projectField()'s pass-through behavior:
ap.was_path_opened_with_suffix/_prefix returned false for dynamic
entries kept only in cp.Opens.Patterns when the rule omitted
profileDataRequired.opens.

Restores a narrower Patterns scan that, in All-mode, considers each
pattern's CONCRETE SUFFIX/PREFIX (text up to the first '*' or '⋯')
rather than the full pattern body. This keeps the spoof-resistance
intent matthyx noted (rejecting raw HasPrefix/HasSuffix over
attacker-influenced pattern strings) while restoring matches for the
dynamic entries the All-mode contract is supposed to cover.

The two segment-iteration sites that classify wildcard tokens now
match the bare "*" literal directly rather than depending on
dynamicpathdetector.WildcardIdentifier (added in storage kubescape#323, not
yet in NA's pinned storage v0.0.258). DynamicIdentifier reference
stays — it's been in storage since long before v0.0.258.

Resolves matthyx review on open.go:125 (2026-05-19).
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
pkg/rulemanager/cel/libraries/applicationprofile/open.go (1)

71-74: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Emit a degradation signal when flags are ignored.

At Line 71, flags are parsed and then dropped without any metric/log signal. That makes path-only fallback invisible in production, unlike the undeclared-literal paths used in suffix/prefix helpers.

Suggested patch
-	// flags projection (OpenFlagsByPath) is out of scope for v1; degrade to path-only matching.
-	if _, err := celparse.ParseList[string](flags); err != nil {
+	// flags projection (OpenFlagsByPath) is out of scope for v1; degrade to path-only matching.
+	parsedFlags, err := celparse.ParseList[string](flags)
+	if err != nil {
 		return types.NewErr("failed to parse flags: %v", err)
 	}
+	if len(parsedFlags) > 0 && l.metrics != nil {
+		l.metrics.IncProjectionUndeclaredLiteral("ap.was_path_opened_with_flags")
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/rulemanager/cel/libraries/applicationprofile/open.go` around lines 71 -
74, The code parses OpenFlagsByPath via celparse.ParseList[string](flags) but
then silently drops flags to degrade to path-only matching; emit a clear
degradation signal when this happens by logging or incrementing a metric right
after successful parse and before returning, e.g., call your existing
logger/metrics helper to record "flags ignored, falling back to path-only"
(include the parsed flags value) so operators can detect the fallback from
OpenFlagsByPath; update the branch handling celparse.ParseList[string](flags) to
log/metricize the degradation before continuing.
pkg/rulemanager/cel/libraries/applicationprofile/ap.go (1)

379-381: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

ap.was_path_opened_with_flags cost estimate overstates v1 work.

Line 380 documents O(f*p) flag comparison, but v1 currently parses flags and performs path-only matching. Keeping cost at 40 can unnecessarily penalize valid expressions under cost budgets.

Suggested patch
 	case "ap.was_path_opened_with_flags":
-		// Cache lookup + O(n) search + dynamic path comparison + O(f*p) flag comparison
-		cost = 40
+		// v1: cache lookup + O(n) path search + dynamic path comparison (+ list parse)
+		// keep aligned with runtime until OpenFlagsByPath matching is implemented.
+		cost = 25
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/rulemanager/cel/libraries/applicationprofile/ap.go` around lines 379 -
381, The cost for the case "ap.was_path_opened_with_flags" overstates current v1
work; update the case to reduce the cost (e.g., from 40 to a lower value such as
10) and amend the comment to accurately state that v1 parses flags but only
performs path-only matching (no O(f*p) flag comparison). Locate the switch case
for "ap.was_path_opened_with_flags" and change the numeric cost and the
explanatory comment to reflect the actual operations: cache lookup + O(n) path
search + flag parsing (not full flag-pair comparisons).
🧹 Nitpick comments (2)
tests/resources/nginx-user-profile-deployment.yaml (2)

20-20: ⚡ Quick win

Consider using a more recent nginx version.

nginx:1.14.2 was released in December 2018. While this may be intentional for specific test scenarios, using a more recent version (e.g., nginx:1.27 or nginx:stable) would avoid potential issues from known vulnerabilities in the 8-year-old image.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/resources/nginx-user-profile-deployment.yaml` at line 20, Update the
nginx image tag in tests/resources/nginx-user-profile-deployment.yaml: replace
the outdated "image: nginx:1.14.2" with a newer, supported tag such as
"nginx:1.27" or "nginx:stable"; after changing the image line, run the test
suite that references this YAML and verify any other manifests or image-pinning
(e.g., deployment templates or CI fixtures) are updated to the same newer tag to
keep tests consistent.

20-20: ⚡ Quick win

Consider digest-pinning for test reproducibility.

The curl deployment uses digest-pinned images while this uses a tag. For consistent integration test behavior, consider pinning to a specific digest like the curl deployment does.

📌 Example: Convert tag to digest

You can get the current digest for nginx:1.14.2:

docker pull nginx:1.14.2
docker inspect nginx:1.14.2 --format='{{index .RepoDigests 0}}'

Then update the manifest:

-        image: nginx:1.14.2
+        image: nginx@sha256:<digest>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/resources/nginx-user-profile-deployment.yaml` at line 20, Replace the
tag-based image reference "image: nginx:1.14.2" with a digest-pinned reference
(same format used by the curl deployment) to ensure reproducible tests; fetch
the current digest with docker pull nginx:1.14.2 and docker inspect nginx:1.14.2
--format='{{index .RepoDigests 0}}' then update the manifest's image value to
that returned digest (e.g., nginx@sha256:...), keeping the rest of the
deployment manifest unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/rulemanager/cel/libraries/applicationprofile/ap.go`:
- Around line 133-154: The comment for
ap.was_path_opened_with_suffix/ap.was_path_opened_with_prefix is stale: update
the doc to reflect that when the projector is in pass-through mode
cp.Opens.Patterns ARE scanned for concrete suffix/prefix matches via the
patternConcreteSuffix and patternConcretePrefix helpers in open.go (so Patterns
can contribute to suffix/prefix answers), and adjust the guidance about when to
declare an Opens projection slice or use ap.was_path_opened(path) accordingly;
reference ap.was_path_opened_with_suffix, ap.was_path_opened_with_prefix,
cp.Opens.Patterns, patternConcreteSuffix, patternConcretePrefix and
SuffixHits/PrefixHits in the updated comment so rule authors aren’t misled.

In `@pkg/rulemanager/cel/libraries/applicationprofile/open_test.go`:
- Around line 16-47: The top-level test comment contradicts the test body:
update the opening paragraph to state that Patterns ARE scanned when
Opens.All==true but using the narrower fallback (concrete tail/head matches via
HasSuffix/HasPrefix; wildcard-only patterns are permissive), to match the
behavior validated by
TestWasPathOpenedWithSuffix_PatternsScannedWithConcreteTail and the related
tests (TestOpenWithSuffixInProfile / TestOpenWithPrefixInProfile) and the
explanations about Opens.Patterns, Opens.Values, Opens.All and precomputed
Opens.SuffixHits/PrefixHits; remove or rewrite the lines that claim Patterns
must not contribute to suffix/prefix answers so the entire comment asserts the
single consistent contract exercised by the test.

---

Outside diff comments:
In `@pkg/rulemanager/cel/libraries/applicationprofile/ap.go`:
- Around line 379-381: The cost for the case "ap.was_path_opened_with_flags"
overstates current v1 work; update the case to reduce the cost (e.g., from 40 to
a lower value such as 10) and amend the comment to accurately state that v1
parses flags but only performs path-only matching (no O(f*p) flag comparison).
Locate the switch case for "ap.was_path_opened_with_flags" and change the
numeric cost and the explanatory comment to reflect the actual operations: cache
lookup + O(n) path search + flag parsing (not full flag-pair comparisons).

In `@pkg/rulemanager/cel/libraries/applicationprofile/open.go`:
- Around line 71-74: The code parses OpenFlagsByPath via
celparse.ParseList[string](flags) but then silently drops flags to degrade to
path-only matching; emit a clear degradation signal when this happens by logging
or incrementing a metric right after successful parse and before returning,
e.g., call your existing logger/metrics helper to record "flags ignored, falling
back to path-only" (include the parsed flags value) so operators can detect the
fallback from OpenFlagsByPath; update the branch handling
celparse.ParseList[string](flags) to log/metricize the degradation before
continuing.

---

Nitpick comments:
In `@tests/resources/nginx-user-profile-deployment.yaml`:
- Line 20: Update the nginx image tag in
tests/resources/nginx-user-profile-deployment.yaml: replace the outdated "image:
nginx:1.14.2" with a newer, supported tag such as "nginx:1.27" or
"nginx:stable"; after changing the image line, run the test suite that
references this YAML and verify any other manifests or image-pinning (e.g.,
deployment templates or CI fixtures) are updated to the same newer tag to keep
tests consistent.
- Line 20: Replace the tag-based image reference "image: nginx:1.14.2" with a
digest-pinned reference (same format used by the curl deployment) to ensure
reproducible tests; fetch the current digest with docker pull nginx:1.14.2 and
docker inspect nginx:1.14.2 --format='{{index .RepoDigests 0}}' then update the
manifest's image value to that returned digest (e.g., nginx@sha256:...), keeping
the rest of the deployment manifest unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd7d7db5-3c3c-4941-a83a-8ca9d4f74a01

📥 Commits

Reviewing files that changed from the base of the PR and between cc59fa0 and e30da5c.

📒 Files selected for processing (6)
  • pkg/rulemanager/cel/libraries/applicationprofile/ap.go
  • pkg/rulemanager/cel/libraries/applicationprofile/open.go
  • pkg/rulemanager/cel/libraries/applicationprofile/open_bench_test.go
  • pkg/rulemanager/cel/libraries/applicationprofile/open_test.go
  • tests/resources/curl-user-profile-wildcards-deployment.yaml
  • tests/resources/nginx-user-profile-deployment.yaml

Comment on lines +133 to +154
// 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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Helper contract comment is out of sync with runtime behavior.

Lines 139-154 still describe cp.Opens.Patterns as effectively not participating in suffix/prefix answers, but open.go now scans patterns via patternConcreteSuffix/patternConcretePrefix in pass-through mode. This doc mismatch can mislead rule authors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/rulemanager/cel/libraries/applicationprofile/ap.go` around lines 133 -
154, The comment for
ap.was_path_opened_with_suffix/ap.was_path_opened_with_prefix is stale: update
the doc to reflect that when the projector is in pass-through mode
cp.Opens.Patterns ARE scanned for concrete suffix/prefix matches via the
patternConcreteSuffix and patternConcretePrefix helpers in open.go (so Patterns
can contribute to suffix/prefix answers), and adjust the guidance about when to
declare an Opens projection slice or use ap.was_path_opened(path) accordingly;
reference ap.was_path_opened_with_suffix, ap.was_path_opened_with_prefix,
cp.Opens.Patterns, patternConcreteSuffix, patternConcretePrefix and
SuffixHits/PrefixHits in the updated comment so rule authors aren’t misled.

Comment on lines +16 to +47
// TestWasPathOpenedWithSuffix_PatternsNotScanned pins the contract from
// the CodeRabbit PR #43 review on open.go:79 (Major). Wildcard-shaped
// entries in cp.Opens.Patterns MUST NOT contribute to suffix/prefix
// answers — their literal text answers the wrong question. A retained
// pattern "/var/log/pods/*/volumes/...." doesn't END with "foo.log"
// even though the concrete open it stands in for might. Only concrete
// paths in cp.Opens.Values are valid sources of suffix/prefix truth in
// pass-through (Opens.All=true) mode.
//
// In projection-active mode (Opens.All=false), the rule manager
// precomputes Opens.SuffixHits / PrefixHits from the spec, which is
// the correct mechanism — those are exercised in
// TestOpenWithSuffixInProfile / TestOpenWithPrefixInProfile.
//
// These tests pin Matthias's upstream PR #811 review contract:
//
// Patterns ARE scanned when Opens.All == true, but with a NARROWER
// fallback than text-level strings.HasSuffix/HasPrefix:
//
// * Pattern with a concrete tail/head (text after/before the last/first
// wildcard segment): match via HasSuffix/HasPrefix on the concrete
// piece — every realisation has that text textually.
// * Pattern ending/starting with a wildcard segment: be PERMISSIVE
// (return true). The concrete realisations could match ANY
// suffix/prefix; refusing would silently regress rules that omit
// profileDataRequired.opens (Matthias's "we still need a narrower
// fallback here instead of ignoring Patterns entirely").
//
// Pre-PR-#811 (CR's HasSuffix-on-Patterns concern) the matcher SKIPPED
// Patterns entirely. That made wildcard-only profiles silently fail
// suffix/prefix queries — the regression Matthias's review reverses.
func TestWasPathOpenedWithSuffix_PatternsScannedWithConcreteTail(t *testing.T) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Top-level test comment is internally contradictory.

Lines 16-23 say patterns must not contribute to suffix/prefix answers, while Lines 32-43 and the test body assert the opposite (patterns are scanned with narrower fallback). Please reconcile this block to a single contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/rulemanager/cel/libraries/applicationprofile/open_test.go` around lines
16 - 47, The top-level test comment contradicts the test body: update the
opening paragraph to state that Patterns ARE scanned when Opens.All==true but
using the narrower fallback (concrete tail/head matches via HasSuffix/HasPrefix;
wildcard-only patterns are permissive), to match the behavior validated by
TestWasPathOpenedWithSuffix_PatternsScannedWithConcreteTail and the related
tests (TestOpenWithSuffixInProfile / TestOpenWithPrefixInProfile) and the
explanations about Opens.Patterns, Opens.Values, Opens.All and precomputed
Opens.SuffixHits/PrefixHits; remove or rewrite the lines that claim Patterns
must not contribute to suffix/prefix answers so the entire comment asserts the
single consistent contract exercised by the test.

@entlein
Copy link
Copy Markdown
Author

entlein commented May 28, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: WIP

Development

Successfully merging this pull request may close these issues.

2 participants