Repository navigation
feat(r0040): add unexpected process arguments rule - #39
Conversation
Adds pkg/rules/r0040-unexpected-process-arguments/ (yaml + README + tests). R0040 fires when an allowed binary is exec'd with an argv vector that was never recorded for that path, consuming ap.was_executed_with_args over the ExecsByPath projection surface and storage's dynamicpathdetector.MatchExecArgs. It stays silent on unknown paths (R0001's domain) and on any recorded argv. Blocked on node-agent#805: the expression uses the 3-arg parse.get_exec_path(args, comm, exepath) overload and ap.was_executed_with_args, neither of which exists in the pinned node-agent (v0.3.98-...-4aefa6517e0a) or in storage v0.0.258. go.mod must bump node-agent to a release carrying the #805 engine and storage to >= v0.0.278 before `go test` can pass. lint-projection, the README check, and `go vet` are green today; `go test` fails only on the missing CEL overload. Signed-off-by: matthyx <matthias.bertschy@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThe PR updates ChangesDependency Refresh
Rule Updates
Sequence Diagram(s)sequenceDiagram
participant ExecveEvent
participant CELEngine
participant ApplicationProfile
participant AlertOutput
ExecveEvent->>CELEngine: evaluate R0040 with event.exepath and event.args
CELEngine->>ApplicationProfile: ap.was_executed(resolved exec path)
CELEngine->>ApplicationProfile: ap.was_executed_with_args(resolved exec path, event.args)
ApplicationProfile-->>CELEngine: match / no match
CELEngine->>AlertOutput: emit alert when path matches and argv pattern is new
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 markdownlint-cli2 (0.22.1)pkg/rules/r0001-unexpected-process-launched/README.mdmarkdownlint-cli2 wrapper config was not available before execution pkg/rules/r0007-kubernetes-client-executed/README.mdmarkdownlint-cli2 wrapper config was not available before execution pkg/rules/r1001-exec-binary-not-in-base-image/README.mdmarkdownlint-cli2 wrapper config was not available before execution 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. Comment |
…igrate rule tests to new mock API Pins node-agent to the pseudo-version of kubescape/node-agent#843 (feat(cel/parse): add 3-arg get_exec_path overload) so the R0040 rule and its test can resolve parse.get_exec_path(args, comm, exepath). Also bumps storage v0.0.258 -> v0.0.278 (MatchExecArgs / CompareExecArgs) and aligns the replace block with node-agent main (inspektor-gadget fork, runtime-spec v1.2.1, syft, stereoscope) so the module graph builds. The node-agent bump (Apr -> Jun main) drifted the test-facing mock API; migrates all rule tests accordingly: * objCache.ApplicationProfileCache().GetApplicationProfile(id) -> objCache.GetApplicationProfile(id) (12 files) * objCache.NetworkNeighborhoodCache().GetNetworkNeighborhood(id) -> objCache.GetNetworkNeighborhood(id) (4 files) * RulePolicyValidator.Validate(ruleId, comm, &profile.Spec.Containers[0]) -> Validate(ruleId, comm, &objectcache.ProjectedContainerProfile{ PolicyByRuleId: profile.Spec.Containers[0].PolicyByRuleId}) (r1011, r1012, r1030 — Validate now takes a ProjectedContainerProfile) Full suite, lint-projection, and the README check pass except one pre-existing R1001 subtest (different_container_name), whose premise (name-based profile isolation) the new RuleObjectCacheMock no longer models — left for an owner decision; it is already covered by the sibling "no application profile" case. Signed-off-by: matthyx <matthias.bertschy@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ndant R1001 subtest
Aligns the sibling exec rules with the exepath-authoritative
parse.get_exec_path(args, comm, exepath) overload (node-agent#843),
matching the reviewed forms from node-agent#805's default-rules.yaml:
R0001, R0007, R1001 — replace the 2-arg get_exec_path + the now-redundant
(exepath == "" || !ap.was_executed(..., exepath)) fallback with the
single 3-arg call. The overload subsumes the fallback: exepath wins
(kernel-authoritative, spoof-resistant), argv[0] only when exepath is
empty, then comm.
R1004 — both get_exec_path call sites bumped to 3-arg; fallback dropped.
R1000 (/dev/shm) — replace get_exec_path(...).startsWith('/dev/shm/')
with a direct event.args[0] check and fix the clause grouping.
Test fixtures for R0001/R0007 now store the kernel-resolved exepath as the
profile Exec.Path (the recording side is exepath-authoritative), so the
"allowed" cases still suppress under the new resolution. READMEs updated
per the rule-yaml change.
Drops the redundant R1001 "different container name" subtest: its premise
(name-based profile isolation) is no longer modeled by RuleObjectCacheMock,
and the case it intended is already covered by the sibling "no application
profile" case.
Signed-off-by: matthyx <matthias.bertschy@gmail.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Regenerates the bundled CRD from pkg/rules/. Picks up the R0040 addition and the 3-arg get_exec_path migration (R0001/R0007/R1000/R1001/R1004), plus pre-existing source drift in rules whose per-rule yaml had been updated without regenerating the CRD (R1003/R1005/R1006/R1011/R1015 name/description/ message). Generated artifact only — no source rule changes here. Signed-off-by: matthyx <matthias.bertschy@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…o-version) node-agent#843 (the 3-arg get_exec_path overload) merged and was released as v0.3.147. Re-pins node-agent from the #843 branch pseudo-version to the release tag; storage stays at v0.0.278. go test ./..., make lint-projection, and the README check are green against the release. Signed-off-by: matthyx <matthias.bertschy@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
pkg/rules/r0040-unexpected-process-arguments/README.md (1)
27-30: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSpecify a language for the fenced code block.
markdownlint (MD040) flags this block. Since it's illustrative CEL, tag it as
text(orcel) to silence the warning and aid rendering.📝 Proposed fix
-``` +```text ap.was_executed(containerId, parse.get_exec_path(args, comm, exepath)) && !ap.was_executed_with_args(containerId, parse.get_exec_path(args, comm, exepath), args)</details> <details> <summary>🤖 Prompt for AI Agents</summary>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/rules/r0040-unexpected-process-arguments/README.mdaround lines 27 - 30,
The README’s fenced example in the r0040-unexpected-process-arguments
documentation is missing a language tag, triggering markdownlint MD040. Update
the code block in the rule documentation to specify a language such as text or
cel so the illustrative CEL snippet renders cleanly; target the fenced block
containing ap.was_executed and ap.was_executed_with_args.</details> <!-- cr-comment:v1:e1403672ea37b9fd84291e14 --> _Source: Linters/SAST tools_ </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>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@go.mod:
- Line 162: The module is forcing github.com/opencontainers/runtime-spec to
v1.2.1 via a replace directive even though primary dependencies like
kubescape/node-agent and kubescape/storage require v1.3.0. Remove the
runtime-spec replace override in go.mod so the resolver can select the required
v1.3.0 version, and verify the resulting dependency graph still satisfies the
imports used by the affected packages.- Line 61: The go.mod dependency on github.com/containerd/containerd is still
pinned to a vulnerable indirect version, so update the module resolution to
force a patched containerd release. Add a replace directive for
github.com/containerd/containerd in go.mod to point to v1.7.33 or later, and
keep the existing indirect requirement aligned so the module graph resolves to
the safer version. Use the existing containerd/containerd entry in go.mod to
locate the change.In
@rules-crd.yaml:
- Around line 332-356: R0040 is enabled even though it depends on the
unavailable ap.was_executed_with_args CEL helper, so update the rule definition
for id R0040 to avoid runtime evaluation errors. Either set the enabled flag to
false in the "Unexpected process arguments" entry, or only re-enable it after
the pinned runtime includes the helper referenced in ruleExpression and
description. Keep the existing ruleExpression and expressions intact; just gate
activation until the dependency is present.- Line 21: The rule expressions are calling a 3-argument parse.get_exec_path
overload that is not available in the pinned node-agent v0.3.147, so update each
affected rule in rules-crd.yaml to use the supported 2-argument
parse.get_exec_path(event.args, event.comm) form, or alternatively bump
node-agent to a version that explicitly registers the 3-argument signature. Make
the change consistently wherever parse.get_exec_path is used, and keep rule
R0040 separate since it also depends on ap.was_executed_with_args.
Nitpick comments:
In@pkg/rules/r0040-unexpected-process-arguments/README.md:
- Around line 27-30: The README’s fenced example in the
r0040-unexpected-process-arguments documentation is missing a language tag,
triggering markdownlint MD040. Update the code block in the rule documentation
to specify a language such as text or cel so the illustrative CEL snippet
renders cleanly; target the fenced block containing ap.was_executed and
ap.was_executed_with_args.</details> <details> <summary>🪄 Autofix (Beta)</summary> Fix all unresolved CodeRabbit comments on this PR: - [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended) - [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes </details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro **Run ID**: `344038c5-c2c5-488d-be0e-85e4d7f083fd` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 8faf6a99f9df0cc439466074d16c4ef344b4b3b3 and 74b33e772b2a9ada4e12aaccca9cda2978efe925. </details> <details> <summary>⛔ Files ignored due to path filters (1)</summary> * `go.sum` is excluded by `!**/*.sum` </details> <details> <summary>📒 Files selected for processing (32)</summary> * `go.mod` * `pkg/rules/r0001-unexpected-process-launched/README.md` * `pkg/rules/r0001-unexpected-process-launched/rule_test.go` * `pkg/rules/r0001-unexpected-process-launched/unexpected-process-launched.yaml` * `pkg/rules/r0002-unexpected-file-access/rule_test.go` * `pkg/rules/r0003-unexpected-system-call/rule_test.go` * `pkg/rules/r0004-unexpected-capability-used/rule_test.go` * `pkg/rules/r0005-unexpected-domain-request/rule_test.go` * `pkg/rules/r0007-kubernetes-client-executed/README.md` * `pkg/rules/r0007-kubernetes-client-executed/kubernetes-client-executed.yaml` * `pkg/rules/r0007-kubernetes-client-executed/rule_test.go` * `pkg/rules/r0009-ebpf-program-load/rule_test.go` * `pkg/rules/r0010-unexpected-sensitive-file-access/rule_test.go` * `pkg/rules/r0011-unexpected-egress-network-traffic/rule_test.go` * `pkg/rules/r0040-unexpected-process-arguments/README.md` * `pkg/rules/r0040-unexpected-process-arguments/rule_test.go` * `pkg/rules/r0040-unexpected-process-arguments/unexpected-process-arguments.yaml` * `pkg/rules/r1000-exec-from-malicious-source/README.md` * `pkg/rules/r1000-exec-from-malicious-source/exec-from-malicious-source.yaml` * `pkg/rules/r1001-exec-binary-not-in-base-image/README.md` * `pkg/rules/r1001-exec-binary-not-in-base-image/exec-binary-not-in-base-image.yaml` * `pkg/rules/r1001-exec-binary-not-in-base-image/rule_test.go` * `pkg/rules/r1003-malicious-ssh-connection/rule_test.go` * `pkg/rules/r1004-exec-from-mount/README.md` * `pkg/rules/r1004-exec-from-mount/exec-from-mount.yaml` * `pkg/rules/r1006-unshare-syscall/rule_test.go` * `pkg/rules/r1009-crypto-mining-related-port/rule_test.go` * `pkg/rules/r1010-symlink-created-over-sensitive-file/rule_test.go` * `pkg/rules/r1011-ld-preload-hook/rule_test.go` * `pkg/rules/r1012-hardlink-created-over-sensitive-file/rule_test.go` * `pkg/rules/r1030-unexpected-io_uring-operation/rule_test.go` * `rules-crd.yaml` </details> <details> <summary>💤 Files with no reviewable changes (1)</summary> * pkg/rules/r1001-exec-binary-not-in-base-image/rule_test.go </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
| github.com/cilium/ebpf v0.20.0 // indirect | ||
| github.com/containerd/cgroups/v3 v3.0.5 // indirect | ||
| github.com/containerd/cgroups/v3 v3.1.2 // indirect | ||
| github.com/containerd/containerd v1.7.30 // indirect |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if these vulnerabilities are addressable via newer versions or if they're constrained by upstream
# List available versions for containerd and docker
go list -m -versions github.com/containerd/containerd | head -5
go list -m -versions github.com/docker/docker | head -5
# Check if node-agent or storage have newer versions that bump these
go list -m -versions github.com/kubescape/node-agent | head -5
go list -m -versions github.com/kubescape/storage | head -5Repository: kubescape/rulelibrary
Length of output: 11683
🏁 Script executed:
#!/bin/bash
# Check dependencies of newer node-agent and storage versions
# Use go mod graph to see what versions they depend on
go mod download github.com/kubescape/node-agent@v0.3.147 2>/dev/null || true
go mod download github.com/kubescape/storage@v0.0.278 2>/dev/null || true
go mod download github.com/kubescape/storage@v0.1.0 2>/dev/null || true
go mod download github.com/kubescape/storage@v0.2.0 2>/dev/null || true
# Check what containerd version node-agent v0.3.147 uses
go mod graph | grep -E "(node-agent.*containerd|storage.*containerd)" | head -20Repository: kubescape/rulelibrary
Length of output: 1288
🏁 Script executed:
#!/bin/bash
# Check if containerd v1.7.33 fixes the mentioned vulnerabilities
# By checking containerd's security advisories or changelog
curl -s https://api.github.com/repos/containerd/containerd/releases | jq -r '.[] | select(.tag_name | test("v1\\.7\\.(3[0-9]|3[0-9]?)")) | {tag_name, name, body}' | head -100Repository: kubescape/rulelibrary
Length of output: 228
🏁 Script executed:
#!/bin/bash
# Check the actual dependency tree in the current project
cat go.mod | grep -E "(containerd|docker)" -A 2 -B 2Repository: kubescape/rulelibrary
Length of output: 1746
🏁 Script executed:
#!/bin/bash
# Check containerd release notes for security fixes in newer 1.7.x versions
curl -s "https://raw.githubusercontent.com/containerd/containerd/v1.7.33/CHANGELOG.md" | grep -A 50 "v1.7.31\|v1.7.32\|v1.7.33\|Security\|CVE" | head -100Repository: kubescape/rulelibrary
Length of output: 159
🏁 Script executed:
#!/bin/bash
# Check what containerd version newer storage versions use
go mod download github.com/kubescape/storage@v0.1.0 2>/dev/null || true
go mod download github.com/kubescape/storage@v0.2.0 2>/dev/null || true
go mod graph | grep "storage.*containerd/containerd" || echo "No containerd/containerd dependency found in storage versions checked"
# Also check the actual go.mod of those versions if available
curl -s "https://raw.githubusercontent.com/kubescape/storage/v0.2.0/go.mod" | grep containerd
echo "---"
curl -s "https://raw.githubusercontent.com/kubescape/storage/v0.1.0/go.mod" | grep containerdRepository: kubescape/rulelibrary
Length of output: 233
🏁 Script executed:
#!/bin/bash
# Check OSV database for specific CVEs related to containerd v1.7.30
# Search for HIGH severity vulnerabilities in containerd 1.7.x
python3 - <<'PY'
import urllib.request
import json
# Search OSV for containerd vulnerabilities
url = "https://api.osv.dev/v1/query"
data = json.dumps({
"package": {
"name": "github.com/containerd/containerd",
"ecosystem": "Go"
},
"version": "1.7.30"
}).encode()
try:
req = urllib.request.Request(url, data=data, headers={'Content-Type': 'application/json'})
response = urllib.request.urlopen(req, timeout=10)
result = json.loads(response.read())
# Check severity and describe findings
vulns = result.get('vulns', [])
high_vulns = []
for v in vulns:
severity = v.get('severity', [])
is_high = any(s.get('type') == 'CVSS_V3' and float(s.get('score', 0)) >= 7.0 for s in severity)
if is_high or any('HIGH' in s.get('type', '') for s in severity):
high_vulns.append({
'id': v.get('id'),
'summary': v.get('details', '')[:200],
'severity': severity
})
print(f"Found {len(vulns)} total vulnerabilities")
print(f"Found {len(high_vulns)} HIGH severity vulnerabilities:")
for hv in high_vulns[:5]:
print(f" - {hv['id']}: {hv['summary']}")
except Exception as e:
print(f"Error querying OSV: {e}")
PYRepository: kubescape/rulelibrary
Length of output: 313
🏁 Script executed:
#!/bin/bash
# Query OSV using curl directly without Python SSL issues
curl -s -H "Content-Type: application/json" \
--data-binary '{"package":{"name":"github.com/containerd/containerd","ecosystem":"Go"},"version":"1.7.30"}' \
"https://api.osv.dev/v1/query" 2>/dev/null | head -1000Repository: kubescape/rulelibrary
Length of output: 20976
🏁 Script executed:
#!/bin/bash
# Check containerd GitHub security advisories for HIGH severity issues in 1.7.x
curl -s "https://github.com/advisories?query=ecosystem%3Agolang+package%3Agithub.life-white.uk%2Fcontainerd%2Fcontainerd+version%3A1.7.30+severity%3Ahigh" 2>/dev/null | grep -i "cve\|advisory\|high" | head -50Repository: kubescape/rulelibrary
Length of output: 15230
🏁 Script executed:
#!/bin/bash
# Check if containerd v1.7.31, v1.7.32, v1.7.33 have security patches
curl -s "https://api.github.com/repos/containerd/containerd/compare/v1.7.30...v1.7.33" 2>/dev/null | grep -i "security\|fix\|cve" | head -30Repository: kubescape/rulelibrary
Length of output: 159
HIGH severity vulnerabilities in indirect containerd dependencies require replacement directive
OSV Scanner confirmed multiple HIGH severity vulnerabilities in containerd/containerd v1.7.30 used by node-agent v0.3.147:
- runAsNonRoot bypass (CVE-2026-46680): Fixed in v1.7.32
- Host-root command execution from image pull (CVE-2026-53488): Fixed in v1.7.33
- CRI checkpoint CDI annotation smuggling (CVE-2026-53492): No fixed version in 1.7.x
- CRI checkpoint local image tag poisoning (CVE-2026-50195): No fixed version in 1.7.x
- Symlink following in CRI checkpoint restore (CVE-2026-53489): No fixed version in 1.7.x
While these libraries are consumed only for type/API access (not runtime orchestration), the vulnerabilities warrant mitigation.
Recommended actions:
-
Add a
replacedirective to force the use ofcontainerd/containerd v1.7.33(or later) to immediately address the patchable vulnerabilities:replace github.com/containerd/containerd => github.com/containerd/containerd v1.7.33 -
Track upstream releases of
kubescape/node-agentfor future updates that may bump the transitive dependency. -
Note that some critical CRI-related vulnerabilities have no fix in the 1.7.x series; consider monitoring containerd v2.x releases if upstream adopts them.
🧰 Tools
🪛 OSV Scanner (2.4.0)
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: containerd CRI checkpoint restore CDI annotation smuggling in github.com/containerd/containerd
(GO-2026-5064)
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: containerd: CRI checkpoint import allows local image tag poisoning in github.com/containerd/containerd
(GO-2026-5338)
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: containerd user ID handling bypass allows runAsNonRoot evasion in github.com/containerd/containerd
(GO-2026-5378)
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: containerd image-triggered runtime DoS via unbounded group parsing in github.com/containerd/containerd
(GO-2026-5475)
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: Arbitrary host CRI log file read via symlink following in CRI checkpoint restore in github.com/containerd/containerd
(GO-2026-5622)
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: containerd CRI — image-config LABEL flows to restart-monitor binary:// logger: host-root command execution from an image pull in github.com/containerd/containerd
(GO-2026-5758)
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: containerd user ID handling bypass allows runAsNonRoot evasion
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: containerd image-triggered runtime DoS via unbounded group parsing
[HIGH] 61-61: github.com/containerd/containerd 1.7.30: containerd CRI — image-config LABEL flows to restart-monitor binary:// logger: host-root command execution from an image pull
🤖 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 `@go.mod` at line 61, The go.mod dependency on github.com/containerd/containerd
is still pinned to a vulnerable indirect version, so update the module
resolution to force a patched containerd release. Add a replace directive for
github.com/containerd/containerd in go.mod to point to v1.7.33 or later, and
keep the existing indirect requirement aligned so the module graph resolves to
the safer version. Use the existing containerd/containerd entry in go.mod to
locate the change.
| github.com/opencontainers/go-digest v1.0.0 // indirect | ||
| github.com/opencontainers/image-spec v1.1.1 // indirect | ||
| github.com/opencontainers/runtime-spec v1.2.1 // indirect | ||
| github.com/opencontainers/runtime-spec v1.3.0 // indirect |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify runtime-spec replace compatibility
# Check which transitive dependency pulls in runtime-spec and if v1.2.1 satisfies it
go mod why -m github.com/opencontainers/runtime-spec
go mod graph | grep 'runtime-spec'Repository: kubescape/rulelibrary
Length of output: 6243
Critical dependency conflict: runtime-spec v1.2.1 replace overrides v1.3.0 requirement from primary dependencies
The replace directive at line 280 forcing github.com/opencontainers/runtime-spec v1.2.1 directly conflicts with the explicit v1.3.0 requirements of github.com/kubescape/node-agent and github.com/kubescape/storage, which are core dependencies for this module.
Forcing v1.2.1 downgrades the API below what these primary dependencies expect, creating a high risk of build failures or runtime incompatibilities where v1.3.0 features or types are utilized.
Remove the replace directive for opencontainers/runtime-spec to allow the dependency resolver to select the required v1.3.0 version.
🤖 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 `@go.mod` at line 162, The module is forcing
github.com/opencontainers/runtime-spec to v1.2.1 via a replace directive even
though primary dependencies like kubescape/node-agent and kubescape/storage
require v1.3.0. Remove the runtime-spec replace override in go.mod so the
resolver can select the required v1.3.0 version, and verify the resulting
dependency graph still satisfies the imports used by the affected packages.
| - name: "Unexpected process arguments" | ||
| enabled: true | ||
| id: "R0040" | ||
| description: "Detects an exec event whose path IS in the application profile but whose argv vector does not match any recorded argv pattern for that path. Consumes ap.was_executed_with_args, which walks the ExecsByPath projection surface and delegates argv comparison to dynamicpathdetector.MatchExecArgs (storage). Stays silent when the path is unknown (R0001 covers that case) and when the argv vector matches any recorded pattern (including the trailing zero-or-more form and the single-arg form); a '*' in a recorded arg is a literal character, not a wildcard." | ||
| expressions: | ||
| message: "'Unexpected process arguments: ' + event.comm + ' with PID ' + string(event.pid) + ' argv=' + event.args.map(a, string(a)).join(' ')" | ||
| uniqueId: "event.comm + '_' + event.exepath + '_' + event.args.map(a, string(a)).join(' ')" | ||
| ruleExpression: | ||
| - eventType: "exec" | ||
| expression: "ap.was_executed(event.containerId, parse.get_exec_path(event.args, event.comm, event.exepath)) && !ap.was_executed_with_args(event.containerId, parse.get_exec_path(event.args, event.comm, event.exepath), event.args)" | ||
| profileDependency: 0 | ||
| profileDataRequired: | ||
| execs: all | ||
| severity: 3 | ||
| supportPolicy: false | ||
| isTriggerAlert: true | ||
| mitreTactic: "TA0002" | ||
| mitreTechnique: "T1059" | ||
| tags: | ||
| - "context:kubernetes" | ||
| - "context:container" | ||
| - "anomaly" | ||
| - "process" | ||
| - "exec" | ||
| - "applicationprofile" |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm was_executed_with_args is registered/available in the pinned deps
rg -nP "was_executed_with_args" -C2
fd -HI '^go\.mod$' --exec sh -c 'echo "== {} =="; grep -nE "node-agent|storage" {}'Repository: kubescape/rulelibrary
Length of output: 159
Disable R0040 until ap.was_executed_with_args is available in the pinned runtime
The rule R0040 is set to enabled: true while depending on the CEL helper ap.was_executed_with_args. This helper requires storage updates from node-agent PR #805, which are not available in the currently pinned runtime versions. Enabling this rule without the helper registered in the CEL environment will cause rule evaluation errors in production.
- Set
enabled: truetoenabled: falseto prevent runtime errors until the dependency is resolved. - Alternatively, update the pinned runtime version to one that includes the
was_executed_with_argshelper before enabling the rule.
Current Rule State
- name: "Unexpected process arguments"
enabled: true # ⚠️ Risk: Helper unavailable in target runtime
id: "R0040"
description: "Detects an exec event whose path IS in the application profile but whose argv vector does not match any recorded argv pattern for that path. Consumes ap.was_executed_with_args..."🤖 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 `@rules-crd.yaml` around lines 332 - 356, R0040 is enabled even though it
depends on the unavailable ap.was_executed_with_args CEL helper, so update the
rule definition for id R0040 to avoid runtime evaluation errors. Either set the
enabled flag to false in the "Unexpected process arguments" entry, or only
re-enable it after the pinned runtime includes the helper referenced in
ruleExpression and description. Keep the existing ruleExpression and expressions
intact; just gate activation until the dependency is present.
…xec rules Replaces the 3-arg parse.get_exec_path(args, comm, exepath) overload in R0001/R0007/R1001/R1004 with a standard-CEL ternary: event.exepath != "" ? event.exepath : parse.get_exec_path(args, comm) Behaviorally identical (exepath wins, else argv[0]/comm) but uses only the 2-arg overload + a ternary, both present on every agent version. So these four rules no longer require node-agent v0.3.147 and need no agentVersionRequirement gate or dual-variant: they keep working on older agents and on the private-node-agent fork (which still imports kubescape/node-agent v0.3.146) with the same spoof-resistant behavior. R0040 is unchanged (it genuinely needs the 3-arg overload + ap.was_executed_with_args from v0.3.147). READMEs and rules-crd.yaml regenerated. go test ./..., lint-projection, and the README check pass. Signed-off-by: matthyx <matthias.bertschy@gmail.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Adds the OSS rule R0040 — Unexpected Process Arguments (
pkg/rules/r0040-unexpected-process-arguments/: YAML + README + tests). R0040 fires when an allowed binary is exec'd with an argv vector that was never recorded for that path, consumingap.was_executed_with_argsover theExecsByPathprojection. Opened as a draft because the expression depends on the node-agent #805 engine, which this repo does not yet pin — see the blocker below.Details
R0040 is the argument-level companion to R0001. Where R0001 fires when an unknown binary runs, R0040 fires when an allowed binary is exec'd with an argv vector that was never recorded for that path. It delegates argv comparison to storage's
dynamicpathdetector.MatchExecArgs, stays silent on unknown paths (R0001's domain) and on any matching argv.⛔ Blocked on kubescape/node-agent#805 — needs a
go.modbump before CI can go greenThe rule expression uses two CEL symbols that only exist on the node-agent #805 branch and are not in the versions this repo currently pins:
parse.get_exec_path(args, comm, exepath)node-agent v0.3.98-…-4aefa6517e0a(Apr 27) lacks itap.was_executed_with_args(containerId, path, args)dynamicpathdetector.MatchExecArgs/CompareExecArgsstorage v0.0.258(need ≥v0.0.278)Before this can merge,
go.modmust bumpnode-agentto a release carrying the #805 engine andstorageto ≥v0.0.278. I did not bumpgo.modhere because that release doesn't exist yet (#805 is unmerged) and I don't want to pin the library to an unmerged feature branch.What passes today vs. what's blocked
Verified locally against the current pins:
make lint-projection— clean (profileDependency: 0+profileDataRequired: execs: all).scripts/check_readmes.sh pkg/rules—OK.go vet ./pkg/rules/r0040-…/— compiles (the test only uses APIs present at the pin).go test ./pkg/rules/r0040-…/— fails only onfound no matching overload for 'parse.get_exec_path' applied to '(list(dyn), string, string)', i.e. precisely the missing #805 overload. Flips green oncego.modis bumped.Design note for review
I kept the 3-arg
get_exec_pathform so R0040 stays byte-identical to the expression validated by node-agent #805's component test (Test_32_UnexpectedProcessArguments). This diverges from the sibling OSS rules here (R0001/R0007/R1001/R1004), which currently use the 2-arg form + an explicitevent.exepathfallback. Plan: migrate those siblings to the 3-arg form in a separate PR (the same migration #805 carries); if we'd rather R0040 adopt the existing 2-arg idiom instead, say so and I'll rework the expression + tests.Tags include
context:containeralongsidecontext:kubernetes, matching R0001 (argv anomaly is meaningful at the container boundary, not only in Kubernetes).rules-crd.yamlis a generated artifact (make generate-rules-crd) and is left untouched.Summary by CodeRabbit