Skip to content

allowing wildcards in exec args#805

Open
entlein wants to merge 35 commits into
kubescape:mainfrom
k8sstormcenter:upstream-pr/sbob-execs
Open

allowing wildcards in exec args#805
entlein wants to merge 35 commits into
kubescape:mainfrom
k8sstormcenter:upstream-pr/sbob-execs

Conversation

@entlein

@entlein entlein commented May 15, 2026

Copy link
Copy Markdown

Overview

Adds a 3-arg parse.get_exec_path(args, comm, exepath) CEL overload
and recording-side resolveExecPath so rules that need to distinguish
a symlinked exec from its underlying binary (busybox utilities,
buildroot-style image where every /bin/* is a symlink to /bin/busybox)
get the absolute argv[0] path when available, falling back to exepath
then the bare comm.

Additional Information

has a sister PR in storage

Checklist before requesting a review

put an [x] in the box to get it checked

  • My code follows the style guidelines of this project
  • I have commented on my code, particularly in hard-to-understand areas
  • I have performed a self-review of my code
  • If it is a core feature, I have added thorough tests.
  • New and existing unit tests pass locally with my changes

Benchmark: all incoming PRs have been benchmarked

Summary by CodeRabbit

  • New Features

    • Rules now prefer kernel-reported executable paths to resist argv[0] spoofing.
  • Bug Fixes

    • Recorded executable identity fixed for symlinked binaries and exec-override cases so it matches the kernel-reported path.
  • Chores

    • Default rules updated to use the safer exec-path variant.
    • Automatic rewriting of legacy rule expressions with operator-visible notices.
    • CI updated to run an additional component test.
  • Tests

    • Expanded unit, integration, lint, and component tests to validate and enforce behavior.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Trust kernel-reported event.exepath first when resolving exec paths. Add a symmetric 3-arg parse helper and CEL overload, auto-rewrite canonical 2-arg helper usages to the 3-arg form before compilation, update shipped rules to pass event.exepath, and add tests and a lint to prevent regressions.

Changes

Exec-path spoofing defense

Layer / File(s) Summary
Recording-side exec-path precedence
pkg/containerprofilemanager/v1/event_reporting.go, pkg/containerprofilemanager/v1/event_reporting_test.go
resolveExecPath comment documents precedence: kernel exepath → non-empty argv[0]comm. Tests added for busybox symlink and exec -a spoofing scenarios to assert kernel-authoritative resolution.
Parse library 3-arg safe overload
pkg/rulemanager/cel/libraries/parse/parse.go, pkg/rulemanager/cel/libraries/parse/parselib.go, pkg/rulemanager/cel/libraries/parse/parsing_test.go
Add getExecPathWithExePath(args, comm, exepath) with precedence returning non-empty exepath or falling back to 2-arg legacy logic; register parse.get_exec_path_with_exepath CEL overload and add tests verifying symmetry with recording-side behavior.
CEL expression rewriting and integration
pkg/rulemanager/cel/expression_rewrite.go, pkg/rulemanager/cel/expression_rewrite_test.go, pkg/rulemanager/cel/cel.go
Add rewriteDeprecatedHelpers to auto-rewrite canonical parse.get_exec_path(event.args, event.comm) to include event.exepath, emit notices for non-upgradeable cases, integrate rewriter into registerExpression, and add unit/integration tests.
Default rules migration and validation
tests/chart/templates/node-agent/default-rules.yaml, pkg/rulemanager/cel/libraries/parse/default_rules_yaml_lint_test.go
Migrate shipped rules to call parse.get_exec_path(..., event.exepath) and add a lint test that scans default-rules.yaml for any remaining 2-arg parse.get_exec_path occurrences and reports line numbers.
Component tests, deployment, workflow, and deps
tests/component_test.go, tests/resources/curl-exec-arg-wildcards-deployment.yaml, .github/workflows/component-tests.yaml, go.mod
Add Test_32_UnexpectedProcessArguments component test and curl deployment, update CI matrix to run the new test, and bump multiple module dependencies in go.mod.

Overlay label late-propagation handling

Layer / File(s) Summary
Refresh-time overlay resolution
pkg/objectcache/containerprofilecache/reconciler.go
Refresh now derives user-defined overlay name/namespace from stored entry or by re-reading pod labels (supports labels appearing after add), and conditionally re-fetches overlay resources with refined fetch/fallback semantics.
Unit test for late label arrival
pkg/objectcache/containerprofilecache/reconciler_test.go
Adds TestRefreshPicksUpLabelAppearingAfterAdd which seeds a cache entry before the user-defined-profile label appears, simulates label arrival, runs refresh, and asserts overlay execs merged and UserAPRef populated.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • kubescape/node-agent#800: Expands resolveExecPath with additional test cases for kernel-authoritative behavior (busybox symlink and exec -a spoofing scenarios), building on the same precedence fix.

Suggested reviewers

  • matthyx
  • YakirOren

Poem

🐰 I sniffed the proc and found the true way,
argv[0] may fib but exepath will stay.
Three-arg helpers march in tidy rows,
Rewrites and tests keep spoofing on its toes.
Hoppity hops for secure exec flows.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'allowing wildcards in exec args' is vague and only partially related to the PR's main change, which is adding a 3-argument CEL overload for exec-path resolution with kernel-authoritative exepath precedence. Consider a more specific title like 'Add 3-arg parse.get_exec_path overload for kernel-authoritative exec resolution' to better reflect the primary change beyond just wildcard argument matching.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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

entlein commented May 16, 2026

Copy link
Copy Markdown
Author

@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

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown
🧩 Analysis chain

@entlein, I'll kick off a fresh full review of PR #805 right away. Let me also pull the PR details to check atomicity and cross-PR coupling.


🏁 Script executed:

#!/bin/bash
# Get PR `#805` details and files changed
gh pr view 805 --repo kubescape/node-agent --json title,body,files,baseRefName,headRefName

echo "---FILES CHANGED---"
gh pr diff 805 --repo kubescape/node-agent --name-only

echo "---PR `#807` files (R0040 args consumer)---"
gh pr diff 807 --repo kubescape/node-agent --name-only 2>/dev/null || echo "Could not fetch `#807`"

echo "---PR `#812` files (ExecsByPath composite-key surface)---"
gh pr diff 812 --repo kubescape/node-agent --name-only 2>/dev/null || echo "Could not fetch `#812`"

echo "---PR `#811` files (was_path_opened CEL helpers)---"
gh pr diff 811 --repo kubescape/node-agent --name-only 2>/dev/null || echo "Could not fetch `#811`"

Length of output: 3046


🏁 Script executed:

#!/bin/bash
echo "=== PR `#805` DIFF ==="
gh pr diff 805 --repo kubescape/node-agent

echo ""
echo "=== PR `#811` open.go section (for overlap check with `#807`) ==="
gh pr diff 811 --repo kubescape/node-agent -- pkg/rulemanager/cel/libraries/applicationprofile/open.go 2>/dev/null || echo "Could not fetch"

echo ""
echo "=== PR `#807` open.go section ==="
gh pr diff 807 --repo kubescape/node-agent -- pkg/rulemanager/cel/libraries/applicationprofile/open.go 2>/dev/null || echo "Could not fetch"

Length of output: 12669


@entlein, the full review has been triggered. Here is my atomicity confirmation and coupling analysis for #805 (resolveExecPath + 3-arg parse):


✅ Atomicity: Confirmed Clean

PR #805 touches exactly two packages:

  • pkg/rulemanager/cel/libraries/parse/ — CEL 3-arg overload
  • pkg/containerprofilemanager/v1/ — recording-side resolveExecPath

No file overlap with any sibling PR in the tier map:

PR Files Overlap with #805
#807 applicationprofile/exec.go, open.go ✅ None
#811 applicationprofile/ap.go, open.go ✅ None
#812 objectcache/containerprofilecache/, projection_types.go ✅ None

🔗 Cross-PR Coupling Flags

Expected (clean tier dependency):

  • #805#807: #807 (R0040 args consumer) consumes the 3-arg parse.get_exec_path overload introduced here. This is correct Tier 2 → Tier 2 sequential dependency. #807 cannot be merged before #805.

Rebase risk (not #805's fault):

  • #811#807: Both PRs modify pkg/rulemanager/cel/libraries/applicationprofile/open.go. Even with correct tier ordering (Tier 0 before Tier 2), #807 will need a rebase on that file after #811 merges.

Cross-repo Tier 2 sibling:

  • #805 ↔ storage#322: The PR body names storage#322 as the sister. resolveExecPath on the recording side produces the key that CompareExecArgs (storage#322) operates on. These need to land together or storage#322 must tolerate the pre-allowing wildcards in exec args #805 key format.

🐛 Code-Level Issues Found

1. Stale docstring in parsing_test.go (misleading)

TestGetExecPath_SymmetryWithRecordingSide's block comment describes the old recording-side precedence:

// That recording function uses
//   1. exepath (kernel-authoritative)
//   2. argv[0] when non-empty
//   3. comm

But after this PR, resolveExecPath uses: absolute argv[0] → exepath → bare argv[0] → comm. The docstring should be updated to match the new 4-tier precedence to avoid confusing future readers about which direction the "symmetry" runs.

2. 2-arg overload back-compat gap (silent footgun)

The existing parse.get_exec_path(args, comm) 2-arg overload still uses:

if argsList[0] != "" {
    return types.String(argsList[0])
}

For the busybox case, this coincidentally returns the right answer (/bin/sh is non-empty). But for spoofing protection (argv[0]="sshd" with no exepath available), it returns the wrong value with no recourse. The SHOULD use 3-arg comment documents the intent, but any existing rules silently get degraded behavior on busybox images without a compile error or warning. Worth considering whether to emit a CEL deprecation notice or add a test that explicitly documents the 2-arg limitation.

3. Minor: CEL arity guard is redundant in parselib.go

if len(values) != 3 {
    return types.NewErr("expected 3 arguments, got %d", len(values))
}

CEL's overload dispatch already enforces the 3-arg signature declared in []*cel.Type{...}, so this guard is unreachable in normal operation. Harmless, but it adds noise.


The PR is structurally sound for its tier scope. The stale docstring (issue 1) is the most actionable item before merge since it will confuse readers tracing the symmetry invariant.

✅ Actions performed

Full review triggered.

@matthyx matthyx moved this to WIP in KS PRs tracking May 26, 2026
// 3. argv[0] when bare and exepath empty (fexecve / AT_EMPTY_PATH).
// 4. comm as last resort.
func resolveExecPath(exepath, comm string, args []string) string {
if len(args) > 0 && len(args[0]) > 0 && args[0][0] == '/' {

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.

argv[0] is still fully user-controlled here, even when it is absolute. The Linux execve(2) contract only says argv[0] should contain the executed filename by convention; it is not kernel-verified. A quick repro is exec -a /bin/sh sleep 2, which yields cmdline=/bin/sh 2 while /proc/<pid>/exe still points to /usr/bin/sleep.

So this new tier-1 rule lets a process masquerade as /bin/sh (or any other approved absolute path) while the real executable is something else, which reopens the spoofing hole the old exepath-first behavior was protecting against.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

ok, now addressed also covered in tests. It changes the behavior now in that it resolves across a symlink, this may break peoples profiles if they use linked executables. but it makes it tamper proof (at least thats my intention) .
there is also a fallback for people that still use the cel rules with 2 args . so the new behavior is for using 3 args

if len(values) != 3 {
return types.NewErr("expected 3 arguments, got %d", len(values))
}
return l.getExecPathWithExePath(values[0], values[1], values[2])

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.

Adding the 3-arg overload alone doesn't actually fix the fork-shell mismatch in this repo: the bundled rule expressions still call parse.get_exec_path(event.args, event.comm) (see tests/chart/templates/node-agent/default-rules.yaml at lines 21, 201, 336, 361, and 434), so they never hit this new event.exepath fallback.

That means cases like sh -c ... still evaluate to bare sh on the rule side while the recording side now stores /bin/sh, so the regression remains until the rule definitions are migrated too.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

should be addressed by my comment above.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/expression_rewrite.go`:
- Around line 39-58: The current rewriteDeprecatedHelpers function uses raw
regexes (twoArgEventGetExecPath / anyTwoArgGetExecPath) to replace text which
can mutate string literals; change this to parse the CEL expression into an AST,
walk the AST to find CallExpr nodes that match the 2-arg parse.get_exec_path
pattern (with event.args/event.comm) and only rewrite those call nodes to the
3-arg form, leaving string literals and other nodes untouched, then serialize
the modified AST back to source and preserve the existing notices logic (append
the same messages when you perform an AST rewrite or when non-upgradable 2-arg
forms are found) so callers of rewriteDeprecatedHelpers get identical notice
semantics while avoiding rewriting inside literals.

In `@pkg/rulemanager/cel/libraries/parse/parsing_test.go`:
- Around line 139-146: Update the contract comment in
TestGetExecPath_SymmetryWithRecordingSide to match the actual resolver
precedence used by resolveExecPath: the kernel-provided exepath is
authoritative; argv[0] is used only when exepath is empty; and comm is used
last. Replace the current wording that implies unconditional argv[0] precedence
with a precise sequence: exepath, then argv[0] if exepath is empty, then comm,
and mention that this is the value stored in the ApplicationProfile.
🪄 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: 0edff09b-d5e8-4cee-9b43-0a704c31c875

📥 Commits

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

📒 Files selected for processing (10)
  • pkg/containerprofilemanager/v1/event_reporting.go
  • pkg/containerprofilemanager/v1/event_reporting_test.go
  • pkg/rulemanager/cel/cel.go
  • pkg/rulemanager/cel/expression_rewrite.go
  • pkg/rulemanager/cel/expression_rewrite_test.go
  • pkg/rulemanager/cel/libraries/parse/default_rules_yaml_lint_test.go
  • pkg/rulemanager/cel/libraries/parse/parse.go
  • pkg/rulemanager/cel/libraries/parse/parselib.go
  • pkg/rulemanager/cel/libraries/parse/parsing_test.go
  • tests/chart/templates/node-agent/default-rules.yaml

Comment on lines +39 to +58
func rewriteDeprecatedHelpers(expression string) (string, []string) {
var notices []string

if twoArgEventGetExecPath.MatchString(expression) {
original := expression
expression = twoArgEventGetExecPath.ReplaceAllString(expression,
"parse.get_exec_path(event.args, event.comm, event.exepath)")
notices = append(notices, fmt.Sprintf(
"auto-rewrote 2-arg parse.get_exec_path → 3-arg with event.exepath fallback; the 2-arg form trusts argv[0] which is user-controllable and bypassable via `exec -a <allowed-path> <real-binary>`. Update RuleBindings to call the 3-arg form explicitly. Original expression: %q",
original))
}

// After auto-rewrite the canonical pattern is gone. Anything still
// matching the 2-arg shape is non-canonical — flag it for operator
// attention but do not modify.
for _, m := range anyTwoArgGetExecPath.FindAllString(expression, -1) {
notices = append(notices, fmt.Sprintf(
"2-arg parse.get_exec_path with non-event arguments cannot be auto-upgraded and remains argv[0]-spoofable: %s",
m))
}

@coderabbitai coderabbitai Bot May 27, 2026

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 | 🟠 Major | 🏗️ Heavy lift

Avoid raw-regex rewrites that can modify CEL string literals.

Line 44 rewrites source text globally, so a literal like "parse.get_exec_path(event.args, event.comm)" is also rewritten. That changes expression semantics unexpectedly. Please rewrite only parsed call-expression nodes (AST-based transform) and leave literals untouched.

🤖 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/expression_rewrite.go` around lines 39 - 58, The current
rewriteDeprecatedHelpers function uses raw regexes (twoArgEventGetExecPath /
anyTwoArgGetExecPath) to replace text which can mutate string literals; change
this to parse the CEL expression into an AST, walk the AST to find CallExpr
nodes that match the 2-arg parse.get_exec_path pattern (with
event.args/event.comm) and only rewrite those call nodes to the 3-arg form,
leaving string literals and other nodes untouched, then serialize the modified
AST back to source and preserve the existing notices logic (append the same
messages when you perform an AST rewrite or when non-upgradable 2-arg forms are
found) so callers of rewriteDeprecatedHelpers get identical notice semantics
while avoiding rewriting inside literals.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

hi rabbit, thats supposed to be a shim, so that users that dont upgrade to the new default rules, dont have a breaking expierence. So should I put a "this will be deprecated in next major version comment"?
Ideally users will use the new tamper proof rules

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@entlein — yes, a comment (both in code and in the operator-visible notice) is the right move here. The shim design is fine; my concern about string literals is theoretically valid but practically very low-risk since nobody writes CEL security rules with string literals that happen to contain the exact call pattern.

Here's what I'd suggest adding to make the intent and lifecycle explicit:

1. A prominent code comment on rewriteDeprecatedHelpers:

// rewriteDeprecatedHelpers is a compatibility shim that auto-upgrades
// deprecated 2-arg parse.get_exec_path calls to the safer 3-arg form.
// It is intentionally implemented as a text-level rewrite rather than an
// AST transform. This trades theoretical risk of rewriting inside a string
// literal (considered negligible for CEL security-rule expressions) for
// zero dependency on the CEL parser at this layer.
//
// DEPRECATION NOTICE: The 2-arg parse.get_exec_path overload is deprecated
// as of <version>. This shim will be removed in the next major version.
// Operators should migrate RuleBindings to the 3-arg form explicitly.

2. Update the operator-visible notice string to include a "deprecated as of X, will be removed in next major" line, e.g.:

"[DEPRECATED as of vX.Y, to be removed in next major] auto-rewrote 2-arg parse.get_exec_path → ..."

This way the shim intent is self-documenting, operators see the signal at log time, and reviewers understand the conscious tradeoff. No AST machinery needed.

Would you like me to open a tracking issue for the removal so it doesn't get forgotten?

🐇 thoughtful nod


🧠 Learnings used
Learnt from: slashben
Repo: kubescape/node-agent PR: 753
File: pkg/sbomscanner/v1/server.go:62-64
Timestamp: 2026-03-23T09:40:51.594Z
Learning: When reviewing Go code that uses Syft’s `CreateSBOMConfig` builder methods (e.g., `WithCatalogers`, `WithCatalogerSelection`, `WithFilesConfig`, etc.), note they use pointer receivers and mutate the `CreateSBOMConfig` in place, then return the same `*CreateSBOMConfig` for chaining. Therefore, expressions like `cfg.WithCatalogers(...)` are functionally correct even if the return value is not captured, as long as `cfg` is a non-nil `*CreateSBOMConfig` (e.g., from `DefaultCreateSBOMConfig()`). Don’t require assignment purely to “preserve” builder return values for this specific Syft type.

Comment thread pkg/rulemanager/cel/libraries/parse/parsing_test.go Outdated
@entlein

entlein commented May 27, 2026

Copy link
Copy Markdown
Author

claude, please create a new e2e benchmark between NA805 and ST322 and link the results here

matthyx
matthyx previously approved these changes May 28, 2026

@matthyx matthyx left a comment

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.

Rechecked the previous blockers. The exec-path resolver is back to kernel-authoritative exepath-first behavior on both the recording and CEL sides, and the bundled default rules now use the 3-arg parse.get_exec_path(..., event.exepath) form. I don't see any remaining blockers in the current diff.

@entlein

entlein commented May 28, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
✅ 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.

entlein pushed a commit to k8sstormcenter/node-agent that referenced this pull request May 28, 2026
Prevents Go MVS from picking up a higher storage version pulled in
transitively by other deps. PR kubescape#805 (parse.get_exec_path 3-arg overload,
event_reporting spoof revert, CEL auto-rewrite shim) does not reference
any post-v0.0.258 storage symbol, so v0.0.258 is the floor — and now
also the ceiling.
entlein pushed a commit to k8sstormcenter/node-agent that referenced this pull request May 28, 2026
Prevents Go MVS from picking up a higher storage version pulled in
transitively by other deps. PR kubescape#805 (parse.get_exec_path 3-arg overload,
event_reporting spoof revert, CEL auto-rewrite shim) does not reference
any post-v0.0.258 storage symbol, so v0.0.258 is the floor — and now
also the ceiling.

Signed-off-by: entlein <einentlein@gmail.com>
@entlein entlein force-pushed the upstream-pr/sbob-execs branch from 5403e02 to d9a6041 Compare May 28, 2026 12:14
@entlein

entlein commented May 28, 2026

Copy link
Copy Markdown
Author

Picking up your earlier ask about an e2e between this PR and kubescape/storage#322 (now merged at 45e3bfc9, tagged v0.0.278).

Starting with the build leg: image build kicked off via the fork's regression harness.

go.mod on this branch tip already pins kubescape/storage v0.0.278 (the #322 merge commit) plus the transitive-dep downgrades (stereoscope v0.1.9, runtime-spec v1.2.1), so the dep-coherence trip that PR #807 hit twice is pre-resolved here. Will post the build outcome as soon as the run terminates, then chain into the e2e/CT result.

Entlein and others added 24 commits June 17, 2026 10:06
Mirrors containerprofilecache.Apply's extractExecsByPath shape so
exec-args unit tests exercise the real wildcard matcher instead of
hitting the absent-key 'no argv constraint' back-compat fallback.
Same per-Path append + nil→empty-slice rules.

Fixes the four TestExecWithArgsWildcardInProfile cases that were
silently returning true (back-compat) and getting marked as failing
expected:false (sh -x wrong flag, ls -l no directory, echo goodbye
world wrong literal anchor, curl --pass alice wrong literal).

Signed-off-by: entlein <einentlein@gmail.com>
…o go mod sync with main

Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
resolveExecPath and getExecPathWithExePath both gained an
"if argv[0] is absolute, trust argv[0] over exepath" tier in fd9e262.
The justification (busybox-image symlink fidelity — record /bin/sh
instead of /bin/busybox) does not survive the argv[0] spoofing case:
`exec -a /bin/sh sleep 2` yields cmdline=/bin/sh while /proc/<pid>/exe
stays /usr/bin/sleep, so the recorded identity is whatever an
attacker chose, not what actually ran. ap.was_executed lookups for
allowed paths then pass for arbitrary binaries.

Reverts both functions to the v0.3.113 precedence:
  1. exepath (kernel-authoritative)
  2. argv[0] non-empty when exepath empty (fexecve / AT_EMPTY_PATH)
  3. comm

Busybox-image profiles record /bin/busybox (kernel-resolved) — the
v0.3.113 behaviour. The symlink-faithful tier never shipped in a
tagged release; only profiles built against fd9e262 depended on it,
all internal.

Adds explicit absolute-argv[0]-spoof regression tests on both sides
(resolveExecPath and getExecPathWithExePath) pinning that
`args=["/bin/sh", …], exepath="/usr/bin/sleep"` resolves to
`/usr/bin/sleep`. Updated the busybox tests on both sides to reflect
the kernel-authoritative semantics.

Addresses matthyx review on event_reporting.go:63 (2026-05-27).

Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
…just maybe

Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
…rage , lets do a benchmark and get this green before pivoting abck to storage

Signed-off-by: entlein <einentlein@gmail.com>
…x, good thing this ll be squashed

Signed-off-by: entlein <einentlein@gmail.com>
…se the cel rule will be slow

Signed-off-by: entlein <einentlein@gmail.com>
…ow various wildcards

Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
Signed-off-by: entlein <einentlein@gmail.com>
@entlein entlein force-pushed the upstream-pr/sbob-execs branch from 7c4f269 to 40647b0 Compare June 17, 2026 10:15
entlein pushed a commit to k8sstormcenter/node-agent that referenced this pull request Jun 17, 2026
regress-upstream-pr-mirror: upstream-pr=kubescape#805 source=upstream-pr/sbob-execs
Comment thread tests/component_test.go
// this entry matches ONLY `echo star *` and must NOT broaden
// to `echo star <other>`. CT-level mirror of storage's
// TestAP_LiteralStarVsDynamic. (busybox + symlink forms.)
{Path: "/bin/echo", Args: []string{"/bin/echo", "star", "*"}},

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@matthyx now testing for the confusion with the former wildcard char (we dont use * anymore)

@entlein

entlein commented Jun 17, 2026

Copy link
Copy Markdown
Author

Component tests: 21/21 passing

Validated this PR rebased on top of #807, with the storage side from kubescape/storage#328 (exec-arg matching moved into storage), on a coherent node-agent + storage image pair built from these branches.

Full matrix green, including the exec-args case:
Test_01, 02, 06, 07, 08, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 32 — all pass (Test_32 exercises the exec-arg wildcard path).

Benchmark for this exact stack is tracked separately (like-for-like against the current merge-base).

@entlein

entlein commented Jun 17, 2026

Copy link
Copy Markdown
Author

Benchmark — the exec-arg move pays off: node-agent memory down ~20%

Like-for-like isolation: storage held constant at kubescape/storage#328 in both arms, so the only variable is this PR's exec-arg commits.

Metric Before (#807 base) After (#807 + this PR) Δ
Avg CPU (cores) 0.205 0.205 −0.3%
Peak CPU (cores) 0.214 0.215 +0.2%
Avg Memory (MiB) 331.0 261.2 −21.1%
Peak Memory (MiB) 333.7 269.5 −19.3%

Quality gate passed (no metric degraded beyond 10%). Moving exec-arg matching out of the node-agent CEL path into storage cuts node-agent memory by ~20% at flat CPU — pairs with the green component-test matrix above.

@matthyx matthyx left a comment

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.

Rechecked the current head. The earlier exec-arg matching blocker in exec.go is fixed now, and the targeted applicationprofile tests are green.

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