Skip to content

refactor: unify example layout + redis-protocol distro comparison (redis/valkey/keydb/dragonfly) - #173

Merged
entlein merged 14 commits into
mainfrom
feat/restructure-release-redis-distros
Aug 1, 2026
Merged

entlein merged 14 commits into
mainfrom
feat/restructure-release-redis-distros

Conversation

@entlein

@entlein entlein commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Scope: example-layout restructure + the redis-protocol distro comparison. No Argo CD changes — those were reverted out of this branch so they can be reviewed separately.

Depends on the inner submodule commits daff078..eeec4a8 (suite-path follow-up, --namespace retarget, metrics.json write) — these need an inner PR before this can merge.

Verdict

valkey, keydb and dragonfly are contrast-identical to each other, and differ from redis on exactly one rule.

distro version raw opens rules fired
redis-oss 8.10.0 20 14 (incl. R1000)
valkey 9.1.1 21 13
keydb 6.3.4 64 13
dragonfly 1.39.0 49 13

The sole discriminator is R1000. The drifted-binary attack succeeded on all four, so it is a detection difference, not an execution one — and R1000/R1004 have been the most environment-sensitive families in this work, so one rule is thin evidence. The defensible claim is the inverse: across 13 of 14 rules the four are indistinguishable, so a SBoB's contrast behaviour transfers between them.

Behaviour is a separate axis — raw baselines of 20 / 21 / 64 / 49 opens. redis and valkey are one open apart; keydb and dragonfly differ materially. Contrast-equivalent ≠ behaviourally equivalent.

poster

Method

All four deploy from one generated template (gen-distro-manifests.py --check fails on drift): identical pod spec, Service name, container name, securityContext and host mounts, one namespace each. Only image and server argv differ. Versions are each project's actual latest — there is no redis 18 or valkey 15. The advertised version is not a discriminator either: valkey reports redis_version 7.2.4 while being 9.1.1, dragonfly reports 7.4.0.

Layout

postgres/ and postgres-vuln/ moved from the repo root into example/; redis unified from example/redis-client/ + loose example/redis-*.yaml into example/redis/. release/ is deliberately not created — it takes a copy of the finished SBoBs later.

CI behaviour is unchanged and verified: every reference rewritten (Makefile, both affected workflows, local-ci-unit.sh, rule-coverage.yaml, README), all moved manifests re-validate, cards still render.

Bugs found on the way

  • --namespace only half-applied — it set cfg.Namespace while the exec resolver and redis port-forward read suite.Target.Namespace, failing as services "redis" not found. This is what blocks reusing one suite across deployments (inner).
  • metrics.json could vanish silently — guarded by a bare if err == nil; the run printed PERFECT while producing nothing, and the gate then blamed the tune (inner).
  • My own comparison tool was wrong — it read the best iteration for the behaviour axis, but collapse depth depends on convergence, so it reported a tuning artifact as a distro difference. It now reads raw-baseline.
  • KS_RUNC could emit a malformed path (make kubescape / make alertmanager broken on Helm v4 (--post-renderer) + KS_RUNC misdetection #172) — --set global.overrideRuntimePath=]*/bin/runc on a machine with no containerd shim. Now only absolute, executable paths are used.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

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

This change relocates example resources under example/, adds PostgreSQL and Redis environments, introduces Redis distro and attack-testing assets, and adds comparison and poster-rendering tools.

Changes

Example environments and validation tooling

Layer / File(s) Summary
Path and deployment wiring
.github/workflows/*, Makefile, README.md, kubescape/*, scripts/local-ci-unit.sh, pkg
Updates example paths, profile selection, Redis stress-test wiring, runc configuration, Kubescape rendering, coverage output, local checks, and the submodule reference.
PostgreSQL environments
example/postgres/*, example/postgres-vuln/*
Adds standard and vulnerable PostgreSQL images, clusters, initialization data, client pods, and services.
Redis environment
example/redis/Dockerfile.redis-vulnerable, example/redis/*.yaml, example/redis/sbobs/*
Adds a patched Redis image, vulnerable and demo deployments, client workloads, services, and a Redis ContainerProfile.
Redis distro environments
example/redis/distros/*
Adds Redis OSS, Valkey, KeyDB, and Dragonfly manifests, a shared generator, functional tests, attack probes, and verdict data.
Redis attack suites and runners
example/redis/redis-tests/*, example/redis/redis-neighbor-attacks.yaml, example/redis/redis-load-stress.yaml
Adds fileless attacks, neighbor attacks, four end-to-end scenarios, load tests, documentation, and sequential and parallel runners.
Redis analysis tools
scripts/compare-distros.py, scripts/render-distro-poster.py
Adds tools that compare distro metrics and render synchronized animated rule-coverage panels.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Runner
  participant Bobctl
  participant Redis
  participant Detection
  Runner->>Bobctl: Run a Redis AttackSuite
  Bobctl->>Redis: Send RESP commands or Lua EVAL payload
  Redis-->>Bobctl: Return attack response
  Bobctl->>Detection: Check expected detection rules
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% 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
Title check ✅ Passed The title clearly summarizes the main changes: unifying the example layout and comparing Redis protocol distributions.
Description check ✅ Passed The description directly explains the layout refactor, distro comparison, validation results, and related fixes.
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.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/restructure-release-redis-distros

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.

@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: 12

🤖 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 @.github/workflows/ci-postgres-vuln-image.yaml:
- Around line 62-63: Update the Docker build context in both Buildx steps to use
the relocated example/postgres-vuln directory, matching the Dockerfile location,
while leaving the existing Dockerfile references and build configuration
unchanged.

In `@example/argocd-server-attacks.yaml`:
- Line 69: Update the Perl script in the command to exit with non-zero status if
the socket connection fails, and modify it to send the TOKEN in an authenticated
Kubernetes API request using the Authorization header instead of only
establishing a socket connection. Ensure the script exits with non-zero status
if either the socket creation or the authenticated API request fails, so that
the && operator correctly prevents echo k8s_pivot_done from executing unless
real authenticated API access succeeds.

In `@example/postgres/cluster.yaml`:
- Around line 19-44: Remove the manual pg_stat_statements CREATE EXTENSION
statement from postInitSQL, while retaining the postInitApplicationSQL setup and
existing PostgreSQL parameters. Do not alter extension management behavior
elsewhere.

In `@example/redis/Dockerfile.redis-vulnerable`:
- Around line 90-95: Replace the version-only check after Redis installation
with a builder-stage smoke test that briefly starts redis-server, executes EVAL
"return tostring(io)" 0, and verifies the result indicates a table before
stopping the server. Ensure the build fails when the sandbox-escape behavior is
not present, while cleaning up the temporary server process.
- Around line 79-88: Validate Patch 5 against the downloaded Redis source and
the Debian CVE-2022-0543 build mechanism, specifically checking whether
script_lua.c contains any lua_pushnil/lua_setglobal(..., "package") sequence.
Remove the misleading no-op sed patch if that pattern is absent, or replace it
with a targeted modification of the actual Debian Lua initialization source so
package remains accessible. Update the Patch 5 comment to accurately describe
the implemented behavior.

In `@example/redis/redis-tests/attack-01-fileless-memfd.yaml`:
- Around line 28-35: Replace the unconditional success prefixes with indicators
that prove each operation succeeded: in
example/redis/redis-tests/attack-01-fileless-memfd.yaml lines 28-35, validate
output from the memfd-backed executable; in
example/redis/redis-tests/attack-11-reverse-shell-perl.yaml lines 27-32, require
“connected” or another verified C2-success result instead of “revshell:”; and in
example/redis/redis-tests/e2e-01-data-exfiltration.yaml lines 168-174, require
confirmed DNS resolution instead of the unconditional “c2:” prefix.

In `@example/redis/redis-tests/e2e-01-data-exfiltration.yaml`:
- Around line 176-182: Isolate Redis state across the affected E2E suites: in
example/redis/redis-tests/e2e-01-data-exfiltration.yaml#L176-L182, remove the
seeded user:42 and session:abc123 keys and explicitly define whether the
exfil-canary fixture is retained; in
example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L59-L64,
replace the initial FLUSHALL dependency with an isolated Redis instance; remove
the shared-database clearing commands before the SSH persistence phase at
`#L96-L99` and before the webshell persistence phase at `#L125-L128`.

In `@example/redis/redis-tests/e2e-04-monitor-credential-sniff.yaml`:
- Around line 35-71: Reorder the e2e monitor scenario so the sniff-monitor-start
session is established before any decoy AUTH or HSET traffic, then send those
decoys through a separate Redis connection. Assert that the monitor stream
captures both credential-bearing commands, and update sniff-cleanup to close the
monitor session after verification.

In `@example/redis/redis-tests/README.md`:
- Around line 8-24: Update the README directory layout entries for attack 11 and
attack 12 to use attack-11-reverse-shell-perl.yaml and
attack-12-credential-harvest.yaml, matching the shipped files and
DETECTION-RULES.md.

In `@example/redis/redis-tests/run-parallel.sh`:
- Around line 32-42: Update run_one() to capture bobctl attack run’s exit status
before the conditional reporting, and add an exit-code-2 branch that reports the
attack as SKIP, matching run-all.sh. Preserve the existing PASS output for
status 0 and FAIL output for all other nonzero statuses, including the captured
status in the failure message.

In `@scripts/compare-distros.py`:
- Around line 138-139: Update the main reporting flow in
scripts/compare-distros.py so that when args.json is set, stdout contains only
the json.dumps payload; suppress the human-readable report or redirect it to
stderr while preserving the existing JSON fields and formatting.
- Around line 34-59: Update load() to detect and skip incomplete metrics data:
return None with the existing warning behavior when entries is empty, when no
tuned entries exist (so raw-baseline records are not used as best), or when no
raw-baseline entry exists (so tuned data is not used for behavior metrics).
Preserve normal selection of the tuned best entry and raw-baseline behavior
entry when both are available.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f20f3b15-a06e-428f-b354-4ceb2558b21a

📥 Commits

Reviewing files that changed from the base of the PR and between a62d56d and 0e12ca9.

⛔ Files ignored due to path filters (2)
  • example/redis/distros/redis-distro-comparison.gif is excluded by !**/*.gif
  • example/redis/redis-killchain.gif is excluded by !**/*.gif
📒 Files selected for processing (49)
  • .github/workflows/ci-bobctl-autotune.yaml
  • .github/workflows/ci-postgres-vuln-image.yaml
  • Makefile
  • README.md
  • example/argocd-server-attacks.yaml
  • example/postgres-vuln/Dockerfile
  • example/postgres-vuln/cluster.yaml
  • example/postgres/cluster.yaml
  • example/redis/Dockerfile.redis-vulnerable
  • example/redis/client.yaml
  • example/redis/distros/VERDICT.txt
  • example/redis/distros/distro-attacks.yaml
  • example/redis/distros/distro-functional-tests.yaml
  • example/redis/distros/dragonfly.yaml
  • example/redis/distros/gen-distro-manifests.py
  • example/redis/distros/keydb.yaml
  • example/redis/distros/redis-oss.yaml
  • example/redis/distros/valkey.yaml
  • example/redis/redis-load-stress.yaml
  • example/redis/redis-neighbor-attacks.yaml
  • example/redis/redis-tests/DETECTION-RULES.md
  • example/redis/redis-tests/E2E-SCENARIOS.md
  • example/redis/redis-tests/README.md
  • example/redis/redis-tests/attack-01-fileless-memfd.yaml
  • example/redis/redis-tests/attack-02-sa-token-exfil.yaml
  • example/redis/redis-tests/attack-03-read-etc-shadow.yaml
  • example/redis/redis-tests/attack-04-unexpected-whoami.yaml
  • example/redis/redis-tests/attack-05-dns-anomaly.yaml
  • example/redis/redis-tests/attack-06-drifted-binary.yaml
  • example/redis/redis-tests/attack-07-exec-devshm.yaml
  • example/redis/redis-tests/attack-08-read-proc-environ.yaml
  • example/redis/redis-tests/attack-09-symlink-shadow.yaml
  • example/redis/redis-tests/attack-10-crypto-mining-dns.yaml
  • example/redis/redis-tests/attack-11-reverse-shell-perl.yaml
  • example/redis/redis-tests/attack-12-credential-harvest.yaml
  • example/redis/redis-tests/e2e-01-data-exfiltration.yaml
  • example/redis/redis-tests/e2e-02-slaveof-rogue-master.yaml
  • example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml
  • example/redis/redis-tests/e2e-04-monitor-credential-sniff.yaml
  • example/redis/redis-tests/run-all.sh
  • example/redis/redis-tests/run-parallel.sh
  • example/redis/redis-vulnerable.yaml
  • example/redis/redis.yaml
  • example/redis/sbobs/cp-redis.yaml
  • kubescape/rule-coverage.yaml
  • pkg
  • scripts/compare-distros.py
  • scripts/local-ci-unit.sh
  • scripts/render-distro-poster.py

Comment thread .github/workflows/ci-postgres-vuln-image.yaml Outdated
Comment thread example/argocd-server-attacks.yaml Outdated
Comment thread scripts/compare-distros.py
Comment thread scripts/compare-distros.py

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 12

🤖 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 @.github/workflows/ci-postgres-vuln-image.yaml:
- Around line 62-63: Update the Docker build context in both Buildx steps to use
the relocated example/postgres-vuln directory, matching the Dockerfile location,
while leaving the existing Dockerfile references and build configuration
unchanged.

In `@example/argocd-server-attacks.yaml`:
- Line 69: Update the Perl script in the command to exit with non-zero status if
the socket connection fails, and modify it to send the TOKEN in an authenticated
Kubernetes API request using the Authorization header instead of only
establishing a socket connection. Ensure the script exits with non-zero status
if either the socket creation or the authenticated API request fails, so that
the && operator correctly prevents echo k8s_pivot_done from executing unless
real authenticated API access succeeds.

In `@example/postgres/cluster.yaml`:
- Around line 19-44: Remove the manual pg_stat_statements CREATE EXTENSION
statement from postInitSQL, while retaining the postInitApplicationSQL setup and
existing PostgreSQL parameters. Do not alter extension management behavior
elsewhere.

In `@example/redis/Dockerfile.redis-vulnerable`:
- Around line 90-95: Replace the version-only check after Redis installation
with a builder-stage smoke test that briefly starts redis-server, executes EVAL
"return tostring(io)" 0, and verifies the result indicates a table before
stopping the server. Ensure the build fails when the sandbox-escape behavior is
not present, while cleaning up the temporary server process.
- Around line 79-88: Validate Patch 5 against the downloaded Redis source and
the Debian CVE-2022-0543 build mechanism, specifically checking whether
script_lua.c contains any lua_pushnil/lua_setglobal(..., "package") sequence.
Remove the misleading no-op sed patch if that pattern is absent, or replace it
with a targeted modification of the actual Debian Lua initialization source so
package remains accessible. Update the Patch 5 comment to accurately describe
the implemented behavior.

In `@example/redis/redis-tests/attack-01-fileless-memfd.yaml`:
- Around line 28-35: Replace the unconditional success prefixes with indicators
that prove each operation succeeded: in
example/redis/redis-tests/attack-01-fileless-memfd.yaml lines 28-35, validate
output from the memfd-backed executable; in
example/redis/redis-tests/attack-11-reverse-shell-perl.yaml lines 27-32, require
“connected” or another verified C2-success result instead of “revshell:”; and in
example/redis/redis-tests/e2e-01-data-exfiltration.yaml lines 168-174, require
confirmed DNS resolution instead of the unconditional “c2:” prefix.

In `@example/redis/redis-tests/e2e-01-data-exfiltration.yaml`:
- Around line 176-182: Isolate Redis state across the affected E2E suites: in
example/redis/redis-tests/e2e-01-data-exfiltration.yaml#L176-L182, remove the
seeded user:42 and session:abc123 keys and explicitly define whether the
exfil-canary fixture is retained; in
example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L59-L64,
replace the initial FLUSHALL dependency with an isolated Redis instance; remove
the shared-database clearing commands before the SSH persistence phase at
`#L96-L99` and before the webshell persistence phase at `#L125-L128`.

In `@example/redis/redis-tests/e2e-04-monitor-credential-sniff.yaml`:
- Around line 35-71: Reorder the e2e monitor scenario so the sniff-monitor-start
session is established before any decoy AUTH or HSET traffic, then send those
decoys through a separate Redis connection. Assert that the monitor stream
captures both credential-bearing commands, and update sniff-cleanup to close the
monitor session after verification.

In `@example/redis/redis-tests/README.md`:
- Around line 8-24: Update the README directory layout entries for attack 11 and
attack 12 to use attack-11-reverse-shell-perl.yaml and
attack-12-credential-harvest.yaml, matching the shipped files and
DETECTION-RULES.md.

In `@example/redis/redis-tests/run-parallel.sh`:
- Around line 32-42: Update run_one() to capture bobctl attack run’s exit status
before the conditional reporting, and add an exit-code-2 branch that reports the
attack as SKIP, matching run-all.sh. Preserve the existing PASS output for
status 0 and FAIL output for all other nonzero statuses, including the captured
status in the failure message.

In `@scripts/compare-distros.py`:
- Around line 138-139: Update the main reporting flow in
scripts/compare-distros.py so that when args.json is set, stdout contains only
the json.dumps payload; suppress the human-readable report or redirect it to
stderr while preserving the existing JSON fields and formatting.
- Around line 34-59: Update load() to detect and skip incomplete metrics data:
return None with the existing warning behavior when entries is empty, when no
tuned entries exist (so raw-baseline records are not used as best), or when no
raw-baseline entry exists (so tuned data is not used for behavior metrics).
Preserve normal selection of the tuned best entry and raw-baseline behavior
entry when both are available.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f20f3b15-a06e-428f-b354-4ceb2558b21a

📥 Commits

Reviewing files that changed from the base of the PR and between a62d56d and 0e12ca9.

⛔ Files ignored due to path filters (2)
  • example/redis/distros/redis-distro-comparison.gif is excluded by !**/*.gif
  • example/redis/redis-killchain.gif is excluded by !**/*.gif
📒 Files selected for processing (49)
  • .github/workflows/ci-bobctl-autotune.yaml
  • .github/workflows/ci-postgres-vuln-image.yaml
  • Makefile
  • README.md
  • example/argocd-server-attacks.yaml
  • example/postgres-vuln/Dockerfile
  • example/postgres-vuln/cluster.yaml
  • example/postgres/cluster.yaml
  • example/redis/Dockerfile.redis-vulnerable
  • example/redis/client.yaml
  • example/redis/distros/VERDICT.txt
  • example/redis/distros/distro-attacks.yaml
  • example/redis/distros/distro-functional-tests.yaml
  • example/redis/distros/dragonfly.yaml
  • example/redis/distros/gen-distro-manifests.py
  • example/redis/distros/keydb.yaml
  • example/redis/distros/redis-oss.yaml
  • example/redis/distros/valkey.yaml
  • example/redis/redis-load-stress.yaml
  • example/redis/redis-neighbor-attacks.yaml
  • example/redis/redis-tests/DETECTION-RULES.md
  • example/redis/redis-tests/E2E-SCENARIOS.md
  • example/redis/redis-tests/README.md
  • example/redis/redis-tests/attack-01-fileless-memfd.yaml
  • example/redis/redis-tests/attack-02-sa-token-exfil.yaml
  • example/redis/redis-tests/attack-03-read-etc-shadow.yaml
  • example/redis/redis-tests/attack-04-unexpected-whoami.yaml
  • example/redis/redis-tests/attack-05-dns-anomaly.yaml
  • example/redis/redis-tests/attack-06-drifted-binary.yaml
  • example/redis/redis-tests/attack-07-exec-devshm.yaml
  • example/redis/redis-tests/attack-08-read-proc-environ.yaml
  • example/redis/redis-tests/attack-09-symlink-shadow.yaml
  • example/redis/redis-tests/attack-10-crypto-mining-dns.yaml
  • example/redis/redis-tests/attack-11-reverse-shell-perl.yaml
  • example/redis/redis-tests/attack-12-credential-harvest.yaml
  • example/redis/redis-tests/e2e-01-data-exfiltration.yaml
  • example/redis/redis-tests/e2e-02-slaveof-rogue-master.yaml
  • example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml
  • example/redis/redis-tests/e2e-04-monitor-credential-sniff.yaml
  • example/redis/redis-tests/run-all.sh
  • example/redis/redis-tests/run-parallel.sh
  • example/redis/redis-vulnerable.yaml
  • example/redis/redis.yaml
  • example/redis/sbobs/cp-redis.yaml
  • kubescape/rule-coverage.yaml
  • pkg
  • scripts/compare-distros.py
  • scripts/local-ci-unit.sh
  • scripts/render-distro-poster.py
🛑 Comments failed to post (8)
example/postgres/cluster.yaml (1)

19-44: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the repository-declared CloudNativePG version and inspect this manifest.
rg -n -i --hidden \
  -g '!*vendor*' -g '!*.lock' \
  'cloudnative-pg|cloudnativepg|cnpg' .

rg -n -C3 \
  'postInitSQL|postInitApplicationSQL|pg_stat_statements|shared_preload_libraries' \
  example/postgres/cluster.yaml

Repository: k8sstormcenter/bob

Length of output: 1255


🌐 Web query:

CloudNativePG postgresql.conf pg_stat_statements track max shared_preload_libraries documentation

💡 Result:

In CloudNativePG, you enable the pg_stat_statements extension by configuring its specific parameters within the Cluster resource. When you define parameters starting with pg_stat_statements. (such as pg_stat_statements.max or pg_stat_statements.track), the CloudNativePG operator automatically performs two actions: it adds pg_stat_statements to the shared_preload_libraries configuration and executes the SQL command CREATE EXTENSION IF NOT EXISTS pg_stat_statements on your databases [1][2]. Example configuration excerpt for your Cluster resource: postgresql: parameters: pg_stat_statements.max: "10000" pg_stat_statements.track: all Key technical considerations: Automatic Management: The operator manages shared_preload_libraries automatically for several well-known extensions, including pg_stat_statements [2]. As soon as it detects a parameter requiring a managed library, it adds the library; if no parameters require it, it removes it [2]. Manual Overrides: While the operator handles managed extensions automatically, you can still provide additional libraries manually via the.spec.postgresql.shared_preload_libraries field in your Cluster resource. The operator merges these with any automatically managed libraries [2]. Limitations: Note that this automatic management is tied to the Cluster resource configuration. Adding extensions via the Database CRD does not automatically update shared_preload_libraries, which can lead to errors if the extension requires it but the corresponding parameter is not set in the Cluster spec [3]. Recommendation: Avoid changing extension images and PostgreSQL configuration (including shared_preload_libraries) at the same time; perform these as separate steps to ensure stability [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,90p' example/postgres/cluster.yaml

Repository: k8sstormcenter/bob

Length of output: 2386


Remove the manual pg_stat_statements extension creation.

CloudNativePG creates pg_stat_statements automatically when the postgresql.parameters section includes a pg_stat_statements.* parameter, and at the same time adds it to shared_preload_libraries. Keep the extension creation here while using extension management.

🤖 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 `@example/postgres/cluster.yaml` around lines 19 - 44, Remove the manual
pg_stat_statements CREATE EXTENSION statement from postInitSQL, while retaining
the postInitApplicationSQL setup and existing PostgreSQL parameters. Do not
alter extension management behavior elsewhere.
example/redis/Dockerfile.redis-vulnerable (2)

79-88: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Patch 5's sed pattern likely never matches; the code path it targets does not exist upstream.

The comment states this removes a lua_pushnil/lua_setglobal(..., "package") pair that "some Redis versions" use to nil out package. Public research on CVE-2022-0543 shows the actual nil-out happens only in Debian's packaging build script via a Lua source string, not in upstream script_lua.c: "The first two are cleared out in the vulnerable version, but the latter wasn't. So, the fix was, you guessed it, to add package=nil to the end of the Lua initialization." The mechanism is "luaL_dostring(lua, "module = nil; require = nil; package = nil");", embedded in Debian's rules file, not a C-level lua_pushnil/lua_setglobal call pair in the vanilla GitHub source this Dockerfile downloads. Also independent of that: even if such a pair existed on two separate lines, the sed range /"package"/{ /lua_pushnil/,/lua_setglobal/d } only evaluates the inner range on lines that already match the outer "package" address, so a lua_pushnil(lua); line without the literal string "package" would never open the range. Given the find ... || true fallback, this patch most likely silently does nothing today. It does not appear to break the escape (the condition it targets likely never existed in the source being patched), but the comment is misleading about what it accomplishes.

Since this touches an external CVE reproduction mechanism, please confirm with a web search whether upstream Redis' script_lua.c for the Debian-vs-vanilla split contains any lua_pushnil/lua_setglobal(..., "package") pattern at all.

🧰 Tools
🪛 Hadolint (2.14.0)

[warning] 86-86: Use 'find .. -print0 | xargs -0 ..' or 'find .. -exec .. +' to allow non-alphanumeric filenames.

(SC2038)


[warning] 86-86: Set the SHELL option -o pipefail before RUN with a pipe in it. If you are using /bin/sh in an alpine image or if your shell is symlinked to busybox then consider explicitly setting your SHELL to /bin/ash, or disable this check

(DL4006)

🤖 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 `@example/redis/Dockerfile.redis-vulnerable` around lines 79 - 88, Validate
Patch 5 against the downloaded Redis source and the Debian CVE-2022-0543 build
mechanism, specifically checking whether script_lua.c contains any
lua_pushnil/lua_setglobal(..., "package") sequence. Remove the misleading no-op
sed patch if that pattern is absent, or replace it with a targeted modification
of the actual Debian Lua initialization source so package remains accessible.
Update the Patch 5 comment to accurately describe the implemented behavior.

90-95: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider verifying the actual sandbox escape at build time, not just the binary version.

The only build-time check is redis-server --version. Given the fragility of the preceding sed-based patches, add a smoke test in the builder stage that starts redis-server briefly and confirms EVAL "return tostring(io)" 0 returns a table, so a future upstream source change that silently breaks one of the patches fails the build instead of shipping a non-functional lab image.

🧰 Tools
🪛 Hadolint (2.14.0)

[info] 95-95: Multiple consecutive RUN instructions. Consider consolidation.

(DL3059)

🤖 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 `@example/redis/Dockerfile.redis-vulnerable` around lines 90 - 95, Replace the
version-only check after Redis installation with a builder-stage smoke test that
briefly starts redis-server, executes EVAL "return tostring(io)" 0, and verifies
the result indicates a table before stopping the server. Ensure the build fails
when the sandbox-escape behavior is not present, while cleaning up the temporary
server process.
example/redis/redis-tests/attack-01-fileless-memfd.yaml (1)

28-35: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Require evidence of the advertised attack result.

Each indicator matches a prefix that the payload adds after io_mod.popen() starts. The suite can pass when the memfd execution fails, when the Perl socket cannot connect, or when DNS lookup fails. This records failed attack paths as successful results.

  • example/redis/redis-tests/attack-01-fileless-memfd.yaml#L28-L35: require output that proves the memfd-backed executable ran.
  • example/redis/redis-tests/attack-11-reverse-shell-perl.yaml#L27-L32: require connected, or another verified C2-success result, instead of revshell:.
  • example/redis/redis-tests/e2e-01-data-exfiltration.yaml#L168-L174: require a successful DNS-resolution result instead of the unconditional c2: prefix.
📍 Affects 3 files
  • example/redis/redis-tests/attack-01-fileless-memfd.yaml#L28-L35 (this comment)
  • example/redis/redis-tests/attack-11-reverse-shell-perl.yaml#L27-L32
  • example/redis/redis-tests/e2e-01-data-exfiltration.yaml#L168-L174
🤖 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 `@example/redis/redis-tests/attack-01-fileless-memfd.yaml` around lines 28 -
35, Replace the unconditional success prefixes with indicators that prove each
operation succeeded: in example/redis/redis-tests/attack-01-fileless-memfd.yaml
lines 28-35, validate output from the memfd-backed executable; in
example/redis/redis-tests/attack-11-reverse-shell-perl.yaml lines 27-32, require
“connected” or another verified C2-success result instead of “revshell:”; and in
example/redis/redis-tests/e2e-01-data-exfiltration.yaml lines 168-174, require
confirmed DNS resolution instead of the unconditional “c2:” prefix.
example/redis/redis-tests/e2e-01-data-exfiltration.yaml (1)

176-182: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Isolate Redis state for each suite.

All sites target the same redis service. E2E-01 leaves seeded keys. E2E-03 deletes the complete database three times. This changes later test results and destroys unrelated data when suites share a target or run in parallel.

  • example/redis/redis-tests/e2e-01-data-exfiltration.yaml#L176-L182: remove user:42 and session:abc123, and define whether the canary is retained as an explicit fixture.
  • example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L59-L64: remove the first FLUSHALL dependency by using an isolated Redis instance.
  • example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L96-L99: do not clear the shared database before the SSH persistence phase.
  • example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L125-L128: do not clear the shared database before the webshell persistence phase.
📍 Affects 2 files
  • example/redis/redis-tests/e2e-01-data-exfiltration.yaml#L176-L182 (this comment)
  • example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L59-L64
  • example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L96-L99
  • example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L125-L128
🤖 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 `@example/redis/redis-tests/e2e-01-data-exfiltration.yaml` around lines 176 -
182, Isolate Redis state across the affected E2E suites: in
example/redis/redis-tests/e2e-01-data-exfiltration.yaml#L176-L182, remove the
seeded user:42 and session:abc123 keys and explicitly define whether the
exfil-canary fixture is retained; in
example/redis/redis-tests/e2e-03-config-save-cron-persistence.yaml#L59-L64,
replace the initial FLUSHALL dependency with an isolated Redis instance; remove
the shared-database clearing commands before the SSH persistence phase at
`#L96-L99` and before the webshell persistence phase at `#L125-L128`.
example/redis/redis-tests/e2e-04-monitor-credential-sniff.yaml (1)

35-71: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the AttackSuite executor before inspecting Redis connection ownership.
ast-grep outline pkg --items all --type function --match 'AttackSuite|Redis|Attack'

# Inspect whether attacks share a connection or create one connection per command.
rg -n -C 6 --glob '*.go' \
  '\bAttackSuite\b|\bMONITOR\b|net\.Dial|DialContext|redis.*command' pkg

Repository: k8sstormcenter/bob

Length of output: 170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Repo tracked files around package and example:"
git ls-files | sed -n '1,200p'

echo
echo "Find test manifest and Redis-related files:"
git ls-files | rg 'redis-tests|redis|e2e-04|attack|redis' || true

echo
echo "Inspect target manifest with line numbers:"
cat -n example/redis/redis-tests/e2e-04-monitor-credential-sniff.yaml | sed -n '1,140p'

Repository: k8sstormcenter/bob

Length of output: 15837


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Search repository for bobctl/attack execution definitions and redis protocol handling:"
git ls-files | rg 'redis-client|redis|client|protocol|attack|exec|custom' | sed -n '1,240p'

echo
echo "Search for redis client implementation and connections:"
rg -n -C 5 --glob '*.{go,ts,js,py}' \
  '\bcustom\b|\bredis\b|\bMONITOR\b|\bAUTH\b|\bCLIENT LIST\b|\bnet\.Dial|DialContext|bufio\.Reader|ReadLine' . \
  | sed -n '1,320p'

echo
echo "Inspect Redis docs/examples for related runner behavior if present:"
if [ -f example/redis/redis-tests/E2E-SCENARIOS.md ]; then
  cat -n example/redis/redis-tests/E2E-SCENARIOS.md | sed -n '1,240p'
fi

Repository: k8sstormcenter/bob

Length of output: 35207


🌐 Web query:

Redis MONITOR command replies to client until disconnected cannot see commands issued before MONITOR started documentation

💡 Result:

The Redis MONITOR command is designed to stream commands processed by the Redis server in real-time, starting only from the moment the MONITOR command is successfully executed [1][2]. It does not provide access to, or a history of, any commands that were issued or processed before the MONITOR session was initiated [1][3]. Because MONITOR functions as a live stream of incoming requests, it is not an audit log and cannot retroactively retrieve past activity [1][4]. Furthermore, its operation has performance implications, as running MONITOR can significantly reduce server throughput [3][4]. Key characteristics regarding its scope include: - Real-time streaming: It only displays commands that arrive at the server while the MONITOR session is active [1][2]. - Security exclusions: Certain administrative commands (e.g., CONFIG, AUTH) and the QUIT command are not included in the MONITOR output for security and functional reasons [1][2]. - Performance impact: Monitoring produces a high overhead and can reduce server performance by 50% or more [3][4]. If you require a history of commands for auditing or forensic purposes, Redis does not provide this through the MONITOR command. Alternative approaches, such as using the Append Only File (AOF) or analyzing replication streams, are typically required for persistent command logging [5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
# Read-only behavioral model of the sequence in e2e-04-monitor-credential-sniff.yaml.
# MONITOR streams commands received after the successful MONITOR call; it cannot see AUTH/HSET issued earlier.
attack_sequence = [
    "sniff-decoy-auth",
    "sniff-decoy-session-write",
    "sniff-monitor-start",
    "sniff-client-list",
    "sniff-cleanup",
]
decoys = ["sniff-decoy-auth", "sniff-decoy-session-write"]
monitor_start = "sniff-monitor-start"

captured_by_monitor = decoys if attack_sequence.index(monitor_start) == len(attack_sequence) - 1 else []

print("target manifest: example/redis/redis-tests/e2e-04-monitor-credential-sniff.yaml")
print(f"attack order: {attack_sequence}")
print(f"MONITOR occurs at index {attack_sequence.index(monitor_start)}")
print(f"Decoy operations before MONITOR: {decoys}")
print(f"Monitor-capture model for this manifest: {captured_by_monitor}; expected empty because decoys are sent before MONITOR starts.")
print("Required behavior: open a managed MONITOR session before sending decoy commands via a second connection, then assert the monitor output contains credential-bearing commands and close the session during cleanup.")
PY

Repository: k8sstormcenter/bob

Length of output: 788


Start MONITOR before sending decoy traffic on a separate connection.

REDIS MONITOR only streams commands processed after MONITOR succeeds. This manifest sends AUTH and HSET before the MONITOR command, so the asserted decoy commands will not appear in the monitor output. Open the monitored session first, send decoys through a second Redis connection, assert the credential-bearing commands in the stream, and close the monitor session during cleanup.

🤖 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 `@example/redis/redis-tests/e2e-04-monitor-credential-sniff.yaml` around lines
35 - 71, Reorder the e2e monitor scenario so the sniff-monitor-start session is
established before any decoy AUTH or HSET traffic, then send those decoys
through a separate Redis connection. Assert that the monitor stream captures
both credential-bearing commands, and update sniff-cleanup to close the monitor
session after verification.
example/redis/redis-tests/README.md (1)

8-24: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the stale attack-11/attack-12 filenames in the directory layout.

This layout lists attack-11-reverse-shell-curl.yaml and attack-12-hardlink-shadow.yaml, but the files actually shipped in this PR are attack-11-reverse-shell-perl.yaml and attack-12-credential-harvest.yaml (also reflected correctly in DETECTION-RULES.md's table). Update the layout so agents picking "an unclaimed attack-NN-*.yaml file" per the Agent Assignment section below don't look for nonexistent files.

📝 Suggested fix
-├── attack-11-reverse-shell-curl.yaml   # R0001 — curl to C2
-├── attack-12-hardlink-shadow.yaml      # R1012 — hardlink over shadow
+├── attack-11-reverse-shell-perl.yaml   # R0001 — perl reverse shell
+├── attack-12-credential-harvest.yaml   # R0001 — credential harvest
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.


🧰 Tools
🪛 markdownlint-cli2 (0.23.1)

[warning] 8-8: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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 `@example/redis/redis-tests/README.md` around lines 8 - 24, Update the README
directory layout entries for attack 11 and attack 12 to use
attack-11-reverse-shell-perl.yaml and attack-12-credential-harvest.yaml,
matching the shipped files and DETECTION-RULES.md.
example/redis/redis-tests/run-parallel.sh (1)

32-42: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

run_one() misreports sandbox-blocked attacks as failures.

run-all.sh treats exit code 2 as SKIP ("sandbox blocked"), but run_one() here has no equivalent branch: any nonzero exit, including 2, is reported as [FAIL]. Running the same attack suite via run-all.sh vs run-parallel.sh therefore produces different PASS/FAIL/SKIP semantics for the same result, which can mislead someone reading the parallel run's summary.

🛠️ Suggested fix to align SKIP handling with run-all.sh
   if bobctl attack run "$f" > "${RESULTS_DIR}/${name}.log" 2>&1; then
     echo "[PASS]  ${name}"
   else
-    echo "[FAIL]  ${name} (exit $?)"
+    rc=$?
+    if [[ ${rc} -eq 2 ]]; then
+      echo "[SKIP]  ${name} (sandbox blocked)"
+    else
+      echo "[FAIL]  ${name} (exit ${rc})"
+    fi
   fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

run_one() {
  local f="$1"
  local name
  name="$(basename "$f" .yaml)"
  echo "[START] ${name}"
  if bobctl attack run "$f" > "${RESULTS_DIR}/${name}.log" 2>&1; then
    echo "[PASS]  ${name}"
  else
    rc=$?
    if [[ ${rc} -eq 2 ]]; then
      echo "[SKIP]  ${name} (sandbox blocked)"
    else
      echo "[FAIL]  ${name} (exit ${rc})"
    fi
  fi
}
🤖 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 `@example/redis/redis-tests/run-parallel.sh` around lines 32 - 42, Update
run_one() to capture bobctl attack run’s exit status before the conditional
reporting, and add an exit-code-2 branch that reports the attack as SKIP,
matching run-all.sh. Preserve the existing PASS output for status 0 and FAIL
output for all other nonzero statuses, including the captured status in the
failure message.

Comment thread example/redis/distros/distro-functional-tests.yaml

@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: 12

🤖 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 `@example/argocd/drive-gitops-workload.sh`:
- Around line 34-43: Update the apply_apps and apply_appset functions to capture
the kubectl apply exit status instead of suppressing failures, while retaining
the existing output redirection if desired. When either Application or
ApplicationSet seeding fails, report the failure and stop the script before
starting the workload window, ensuring a failed seed cannot produce an
idle-control-plane profile.
- Line 25: Validate DURATION immediately after its default assignment and before
the arithmetic that computes END, accepting only a non-negative integer and
exiting with a clear error for invalid input. Ensure the existing loop uses a
validated numeric duration so malformed arguments cannot produce an empty END or
broken test.

In `@example/argocd/sbobs/cp-argocd-application-controller.yaml`:
- Around line 946-955: Remove or correct the misattributed :6443/metrics inbound
records: in example/argocd/sbobs/cp-argocd-application-controller.yaml#L946-L955
use port 8082, in
example/argocd/sbobs/cp-argocd-applicationset-controller.yaml#L108-L118 use port
8080, and in
example/argocd/sbobs/cp-argocd-notifications-controller.yaml#L120-L130 use port
9001; remove the record from
example/argocd/sbobs/cp-argocd-repo-server.yaml#L1305-L1314.
- Around line 931-934: Confirm whether CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH,
and CAP_NET_ADMIN are genuinely used by each container rather than inherited
from the node-agent or runtime bounding set; if not, remove the capabilities
entries. Apply the same decision in
example/argocd/sbobs/cp-argocd-application-controller.yaml lines 931-934,
cp-argocd-applicationset-controller.yaml lines 104-107,
cp-argocd-notifications-controller.yaml lines 116-119,
cp-argocd-repo-server.yaml lines 1300-1303, and cp-argocd-server.yaml lines
104-107, retaining only capabilities attributable to the repo-server binaries
where applicable.
- Around line 169-789: Collapse the per-run discovery cache entries in the
manifest’s file-path configuration by replacing random numeric suffixes and the
hard-coded 10.43.0.1 API-server segment with dynamic path wildcards. Apply this
to the discovery cache paths ending in serverresources.json and related
discovery metadata, preserving the existing flags while reducing the artifact to
a single reusable wildcard pattern.
- Around line 24-120: Replace the truncated root-level certificate paths in the
profile’s file-access entries with the complete
/usr/share/ca-certificates/mozilla paths, matching the full filenames recorded
in cp-argocd-repo-server.yaml; alternatively regenerate the profile using the
corrected capture. Remove the invalid entries such as /.crt, /crt, /rt, and /t
while preserving the existing open flags.

In `@example/argocd/sbobs/cp-argocd-repo-server.yaml`:
- Around line 20-53: The exec records contain volatile per-run values that
prevent argument matching, and the helm template record is truncated by a bare
--api-versions flag. Update the affected exec entries in the repo-server
configuration to match only stable arguments, collapsing or removing UUIDs, temp
suffixes, generated key IDs, pod/PID details, object counts, and commit SHAs;
alternatively record only each entry’s path. Also remove the incomplete helm
argument record or ensure every --api-versions flag has its value.
- Around line 1340-1354: Update the podSelector for the internal port 8081
ingress entry to retain only app.kubernetes.io/name:
argocd-application-controller. Remove apps.kubernetes.io/pod-index and
statefulset.kubernetes.io/pod-name so all application controller replicas match
the selector.

In `@kubescape/values.yaml`:
- Around line 21-22: Update the driver’s default DURATION used by
example/argocd/drive-gitops-workload.sh to exceed the 15m learningPeriod
configured in kubescape/values.yaml, preserving enough runtime margin for
startup delays and the learning window’s tail.

In `@Makefile`:
- Around line 291-298: Update KS_RUNC_STOCK to classify the documented stock
paths /usr/bin/runc and /var/lib/rancher/k3s/data/current/bin/runc as stock
(yes) before applying the existing absolute-and-executable validation for custom
paths. Preserve the current fallback behavior for malformed, non-absolute, or
non-executable values so KS_RUNC_FLAGS leaves the override empty for all stock
paths.

In `@scripts/sbob-from-learned.py`:
- Around line 51-53: Update scripts/sbob-from-learned.py lines 51-53 so the
opens filtering normalizes Kubernetes service-account timestamp/random directory
segments to a stable or dynamic path while preserving other paths. Apply the
corresponding path replacements to the ca.crt, namespace, and token entries in
example/argocd/sbobs/cp-argocd-application-controller.yaml lines 892-900,
cp-argocd-applicationset-controller.yaml lines 73-81,
cp-argocd-notifications-controller.yaml lines 85-93, and cp-argocd-server.yaml
lines 73-81.
- Around line 31-37: Update is_over_broad to reject paths with any leading
wildcard segment, including /*/token, rather than only paths whose every segment
is a wildcard. Preserve handling of empty paths and use WILDCARD_SEGMENTS when
identifying wildcard segments.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e138d947-b405-4800-8219-428d5064c672

📥 Commits

Reviewing files that changed from the base of the PR and between 0e12ca9 and ae8a6c9.

📒 Files selected for processing (10)
  • Makefile
  • example/argocd-repo-server-attacks.yaml
  • example/argocd/drive-gitops-workload.sh
  • example/argocd/sbobs/cp-argocd-application-controller.yaml
  • example/argocd/sbobs/cp-argocd-applicationset-controller.yaml
  • example/argocd/sbobs/cp-argocd-notifications-controller.yaml
  • example/argocd/sbobs/cp-argocd-repo-server.yaml
  • example/argocd/sbobs/cp-argocd-server.yaml
  • kubescape/values.yaml
  • scripts/sbob-from-learned.py
💤 Files with no reviewable changes (1)
  • example/argocd-repo-server-attacks.yaml

Comment thread example/argocd/drive-gitops-workload.sh Outdated
Comment thread example/argocd/drive-gitops-workload.sh Outdated
Comment thread example/argocd/sbobs/cp-argocd-application-controller.yaml Outdated
Comment thread example/argocd/sbobs/cp-argocd-application-controller.yaml Outdated
Comment thread example/argocd/sbobs/cp-argocd-application-controller.yaml Outdated
Comment thread example/argocd/sbobs/cp-argocd-repo-server.yaml Outdated
Comment thread kubescape/values.yaml Outdated
Comment thread Makefile Outdated
Comment thread scripts/sbob-from-learned.py Outdated
Comment thread scripts/sbob-from-learned.py Outdated

@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: 1

🤖 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 `@scripts/sbob-from-learned.py`:
- Line 86: Update the input parsing in the script’s main flow where spec is
loaded from args.src: replace JSON-only parsing with yaml.safe_load, while
preserving extraction of the "spec" key and compatibility with both YAML and
JSON artifacts.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f446a64c-7eb1-48f0-9f9e-b0a4f2a09328

📥 Commits

Reviewing files that changed from the base of the PR and between ae8a6c9 and e472148.

📒 Files selected for processing (8)
  • example/argocd/sbobs/cp-argocd-application-controller.yaml
  • example/argocd/sbobs/cp-argocd-applicationset-controller.yaml
  • example/argocd/sbobs/cp-argocd-notifications-controller.yaml
  • example/argocd/sbobs/cp-argocd-repo-server.yaml
  • example/argocd/sbobs/cp-argocd-server.yaml
  • kubescape/default-rules.yaml
  • pkg
  • scripts/sbob-from-learned.py

Comment thread scripts/sbob-from-learned.py Outdated

@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: 1

🤖 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`:
- Line 1: Update the pkg gitlink to a commit that includes the required CI
files, specifically pkg/go.mod and pkg/main.go, ensuring it points to the
intended commit eeec4a8a2b99917ead5123352358bb72a6b5bdfc rather than the
incomplete checked-out commit.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: de5a832e-e2d2-40a6-9f65-8fc1fc12bb97

📥 Commits

Reviewing files that changed from the base of the PR and between e472148 and f7fe610.

📒 Files selected for processing (1)
  • pkg

Comment thread pkg Outdated
entlein added a commit that referenced this pull request Jul 31, 2026
The restructure moved postgres-vuln/ under example/. I updated the Dockerfile
path in both Buildx steps but left `context: postgres-vuln`, which no longer
exists — so the image build failed on a directory that had moved out from under
it. Caught by CodeRabbit on #173.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
entlein added a commit that referenced this pull request Jul 31, 2026
--json documented itself as machine-readable but printed the JSON blob
after the full human report, so anything piping stdout into jq had to strip
the preceding text first. The report now goes to stderr when --json is set,
leaving stdout valid JSON on its own; without the flag nothing changes.

Reported by CodeRabbit on #173.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
@entlein

entlein commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

The pkg gitlink finding is a false positive. pkg is a submodule pointing at the private entlein/bob repo, so the review sandbox cannot clone it and sees an empty directory — that is what produced "missing pkg/go.mod and pkg/main.go", not a bad gitlink.

CI proves the gitlink resolves: the Build bobctl job checks out with submodules: recursive + ENTLEIN_PAT, and its Verify layout step runs ls pkg/go.mod pkg/main.go before building. That job is green on fda2e36, as are all 8 jobs.

The pointer has since been advanced from eeec4a8 to 2a2b337 (main's tip, after entlein/bob#59 and #60 merged). The trees are identical, so it is pointer hygiene rather than a content change.

Also addressed since the last review:

  • compare-distros.py --json now emits standalone JSON on stdout, with the readable report on stderr.
  • The postgres CI failure is fixed at the root: the profile-discovery loop had a bare head -1 fallback that accepted pg-1-bootstrap-controller (a short-lived init container) on the first poll and tuned the wrong container, which is why the leg reported 21 missed detections with 0 false positives. Only a profile_match hit now ends the wait. postgres is green at score 0.

The last review covered up to f7fe610; head is now fda2e36.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

@entlein Acknowledged. The pkg finding was invalid because the review sandbox does not clone the private submodule. I will not treat the absent submodule files as a gitlink defect.

I will review the current head, including the JSON output separation and the PostgreSQL profile-discovery change.


✏️ Learnings added
Learnt from: entlein
URL: https://github.com/k8sstormcenter/bob/pull/173

Timestamp: 2026-07-31T21:18:06.621Z
Learning: In this repository, `pkg` is a private Git submodule. The review sandbox does not clone submodules, so missing `pkg/go.mod` or `pkg/main.go` in the sandbox does not indicate a broken gitlink. CI validates the submodule by checking it out recursively with `ENTLEIN_PAT` and verifying these files before building `bobctl`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

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.

@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: 1

🤖 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 `@kubescape/post-render.sh`:
- Around line 36-51: Update the stream rewrite in the Python block of
post-render.sh to capture the result of replacing networkStreamingEnabled and
verify exactly one occurrence was changed. Add a guard matching the existing
mount-rewrite patched-count handling, and stop or report the same failure
condition when the count is not one, while preserving the dependency-free
behavior for the no-MNT path.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 90316d05-5c39-490a-a653-b19bb41b5cc7

📥 Commits

Reviewing files that changed from the base of the PR and between f7fe610 and fda2e36.

📒 Files selected for processing (7)
  • .github/workflows/ci-bobctl-autotune.yaml
  • .github/workflows/ci-postgres-vuln-image.yaml
  • Makefile
  • kubescape/force-network-streaming.sh
  • kubescape/post-render.sh
  • pkg
  • scripts/compare-distros.py
💤 Files with no reviewable changes (1)
  • kubescape/force-network-streaming.sh

Comment thread kubescape/post-render.sh
entlein added a commit that referenced this pull request Jul 31, 2026
The replace() was unguarded, so a chart change that renamed or reformatted
the field would turn the rewrite into a silent no-op — which is precisely
the bug this script exists to prevent. The flag renders false, the profile's
network shape stays inert, and R0005/R0011 never fire while the install
looks healthy.

The three cases are now distinguished: the field present as false is
rewritten; already true is passed through untouched (a cloud-submit stack
legitimately renders it that way); absent entirely is a hard error, because
at that point the rewrite is doing nothing and detection would go quietly
dead.

Reported by CodeRabbit on #173.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
@entlein

entlein commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

The networkStreamingEnabled match-count guard is implemented in dca5f80, pushed after your 21:24 review. You were right that an unguarded replace() could no-op silently — that is precisely the failure this script exists to prevent, so it now distinguishes three cases:

  • false present → rewritten (normal case)
  • already true → passed through untouched; a cloud-submit stack legitimately renders it that way, so this must not be an error
  • field absent entirely → hard error, because at that point the rewrite does nothing and R0005/R0011 would go quietly dead

Verified against synthetic input for all three, plus the real chart on both the default and KS_RUNC_MNT paths — neither false-trips (true=1 false=0, 68 docs valid, mount still scoped to the node-agent DaemonSet only).

CI is green on dca5f80, all 9 checks.

For the record, the webapp leg did fail once on this commit and then passed on a re-run of the same commit. That leg is intermittently flaky — it has also failed on main (6541f1e, 07-30). In the failed run alert volume was normal (101 vs 102) and the same profile was selected, but late matches dropped 116 → 18, i.e. the alerts arrived and the matcher did not attribute them. I have not touched that gate in this PR; flagging it as pre-existing rather than folding a threshold change in here.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- HEAD ---'
git rev-parse --short HEAD
echo '--- recent commits ---'
git log --oneline -8
echo '--- post-renderer outline ---'
ast-grep outline kubescape/post-render.sh --items all || true
echo '--- post-renderer content ---'
cat -n kubescape/post-render.sh
echo '--- changes since dca5f80 parent ---'
git diff --check dca5f80^..dca5f80
git diff --find-renames -- kubescape/post-render.sh dca5f80^..dca5f80

Length of output: 7185


@entlein I verified dca5f80.

kubescape/post-render.sh now handles all three cases correctly:

  • It rewrites "networkStreamingEnabled": false to true.
  • It preserves an already-true field.
  • It fails when neither field exists.

The hard failure occurs before the default output path or the KS_RUNC_MNT path continues. The mount logic remains restricted to exactly one node-agent DaemonSet. git diff --check reports no whitespace errors.

I found no remaining issue in this update. The reported webapp result is consistent with a pre-existing intermittent failure and does not require a change in this PR.


✏️ Learnings added
Learnt from: entlein
URL: https://github.com/k8sstormcenter/bob/pull/173

Timestamp: 2026-07-31T23:19:48.837Z
Learning: In `kubescape/post-render.sh`, `networkStreamingEnabled` must be present in the rendered Helm manifest. The post-renderer rewrites `false` to `true`, accepts an already-true value for cloud-submit stacks, and must fail if the field is absent. Otherwise, the network profile shape is inert and rules R0005 and R0011 can silently stop firing.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

✅ Action performed

Review finished.

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

entlein commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resolve

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Comments resolved and changes approved.

entlein and others added 10 commits August 1, 2026 13:23
…ample/

Two layout inconsistencies:

  * redis was split across example/redis-client/ (manifests + sbob + gif) and
    loose example/redis-*.yaml files, so "where is redis" had two answers.
  * postgres/ and postgres-vuln/ sat at the repo ROOT while every other app
    lived under example/.

Now example/<app>/ holds each app's manifests and assets, and the attack and
functional suites stay flat at example/ level exactly as they are for every
other app — so redis stops being the odd one out in both directions.

CI behaviour is unchanged. Every reference was rewritten and verified: the
Makefile deploy targets, both affected workflows (the postgres-vuln image
build's path filter and Dockerfile location, and the redis load-stress
functional tests), local-ci-unit.sh, rule-coverage.yaml and the README. All
moved manifests re-validate, and the rule-coverage cards still render.

Also fixes a real defect the pattern guardrail caught once the suites could
load again: argocd-server's cve-2023-22482-authbypass-k8s-pivot ended with
"; echo k8s_pivot_done", an UNCONDITIONAL sentinel — the attack reported
success even when the perl pivot failed outright. Now gated on perl exiting 0.

Remaining unit failures are the pre-existing elk/misp ones from the
pkg/nonmigrated archival in 484fac2; this change adds none and removes one.

release/ is deliberately NOT created yet — it takes a copy of the finished
SBoBs once the distro matrix is validated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
The question is whether contrast testing actually distinguishes valkey from
redis. That only has a defensible answer if the distros are the ONLY variable,
so the deployments are GENERATED from one template rather than hand-written:
identical namespace shape, identical Service name (redis) and container name
(redis), identical securityContext and host mounts, one namespace each.
`gen-distro-manifests.py --check` fails on drift, so the invariant is enforced
rather than asserted. What legitimately differs is documented in the generator:
image, server argv, and whether a redis.conf applies (dragonfly is a
reimplementation, not a fork, and takes flags instead).

Versions are each project's actual latest release as of 2026-07-31 — there is
no "redis 18" or "valkey 15":

  redis      8.10.0     valkey     9.1.1
  keydb       6.3.4     dragonfly  1.39.0

All four answer PING. Worth noting what they claim to be: valkey reports
redis_version 7.2.4 for compatibility while being 9.1.1, and dragonfly reports
7.4.0 — so the advertised version is not a usable discriminator.

First result, at the profile level, before any attack runs:

  redis-oss  20 opens   /usr/local/bin/redis-server
  valkey     21 opens   /usr/local/bin/valkey-server
  keydb      64 opens   /usr/local/bin/keydb-server
  dragonfly  49 opens   /usr/local/bin/dragonfly

redis and valkey are one open apart; keydb and dragonfly differ materially.

compare-distros.py reports the two axes separately — CONTRAST (which rules
fired, the axis that decides whether a SBoB transfers) and BEHAVIOUR (what the
learned profile contains). Collapsing them into one verdict would hide the
interesting case: contrast-equivalent distros with different baselines.

render-distro-poster.py composes an A4 four-panel poster by calling the SAME
card renderer per panel, animating them in lockstep so a row can be read across
at one instant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
VERDICT: valkey, keydb and dragonfly are contrast-IDENTICAL to each other, and
differ from redis on exactly ONE rule.

  distro      raw opens   rules fired
  redis-oss          20   14  (incl. R1000)
  valkey             21   13
  keydb              64   13
  dragonfly          49   13

Every non-redis distro fired the same 13 rules. The sole discriminator is R1000
(process executed from malicious source), green on redis and red on the other
three. The drifted-binary attack SUCCEEDED on all four — so this is a detection
difference, not an attack-execution difference.

Treat that single-rule result with care: R1000/R1004 are the rule families that
have proven most environment-sensitive across this work, so one rule is a thin
basis for "valkey differs from redis". The defensible claim is the opposite one:
across 13 of 14 rules the four are indistinguishable, and a SBoB's contrast
behaviour transfers between them.

Behaviour is a different story: raw baselines run 20 / 21 / 64 / 49 opens, so
redis and valkey are one open apart while keydb and dragonfly are materially
different. Contrast-equivalent does not imply behaviourally equivalent, which is
why the two axes are reported separately.

The comparison tool needed a methodology fix: it read the BEST iteration for the
behaviour axis, but how far the minimiser collapses a profile depends on whether
that distro converged — redis-oss reached score 0 and got collapsed to 1 open
while the others stopped at their baseline. That reported a tuning artifact as a
distro difference. Behaviour now reads raw-baseline.

The poster stacks four full rule-coverage cards on A4, animating in lockstep so
a row can be read across at one instant; R1000 is the one tile that differs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
Reported from a multi-node client machine with no containerd shim: the
detection emitted a mangled value that went straight through to helm as

    --set global.overrideRuntimePath=]*/bin/runc

which silently misconfigures node-agent's runc fanotify marking — the exact
failure mode the override exists to PREVENT.

The guard no longer trusts the regex: a value is only used when it is absolute
AND executable on this machine. Empty, relative, mangled or non-existent all
fall back to "stock" and override nothing, which is the safe default since a
stock layout is what IG already knows.

Also worth recording why the first attempt at this failed: Make ends
$(shell ...) at the first unbalanced ')', so a `case` statement truncates
silently. The snippet is deliberately paren-free and says so.

Verified: real k3s runc overrides; garbage, empty, relative and
absolute-but-missing all correctly decline to override.

The Helm v4 half of #172 is NOT addressed here — it needs a decision on how to
force networkStreamingEnabled without --post-renderer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
RCA: the profiles were learned from an Argo CD with ZERO Applications. The
2-minute window saw process startup and nothing else — no clone, no render, no
reconcile. So they encoded Argo CD idling, and every real GitOps operation fell
outside the baseline. That is the reporter's "thousands of alerts".

Worse than omission, the suite asserted the workload's own job as attack:
render-git-spawn / render-helm-spawn / render-kustomize-spawn claimed R0001 on
git, helm and kustomize. For a GitOps controller those three ARE the primary
function. Removed.

What changed:

  learn window   2m -> 15m, because exercising every component takes longer
                 than two minutes
  workload       example/argocd/drive-gitops-workload.sh drives the DEFAULT
                 actions of a real install for the whole window: 3 Applications
                 across the plain/helm/kustomize render paths, an ApplicationSet
                 so the applicationset-controller generates rather than idles,
                 a transient app for the create->render->sync->delete lifecycle,
                 hard refreshes to defeat the manifest cache, plus the API read
                 path and the metrics endpoints Prometheus scrapes

Result, repo-server:

  execs   5 -> 10 distinct binaries, now including git, /usr/lib/git-core/git,
          git-remote-http, helm and kustomize
  opens  51 -> 255
  egress  null -> github.com:443, DNS and redis declared

argocd-server 7->21 opens, application-controller 21->188.

Over-broad opens are DROPPED, not rewritten: node-agent's own dynamic-path
collapsing degenerates to a bare `/*` and `/⋯` under git-checkout churn, and a
path with no literal segment matches /etc/shadow and the SA token — shipping it
would blind R0002/R0006/R0008/R0010. It carries no information, so there is
nothing to preserve by keeping it.

sbob-from-learned.py does that conversion and strips learned bookkeeping, so a
shipped SBoB is clean YAML with one annotation and no commentary.

Still open: nothing in the pipeline checks that a learn window exercised the
workload's primary function. Score 0, contrast 0-Blind and the coverage card
were all green against an idle baseline. That gap is what allowed this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
…cessAllowed

Verified the reporter's scenario end to end rather than assuming the re-learn
was sufficient — apply the SBoBs, bind them, drive benign GitOps, count alerts.
Two more defects surfaced that the re-learn alone did not fix.

1. Kubernetes projected volumes (SA token, ConfigMaps, Secrets) write each
   generation into a "..<timestamp>" directory and atomically re-point ..data at
   it. The learned profile captured the literal timestamp from training time, so
   after the first rotation the workload's OWN token read fell outside the
   baseline and fired R0002 continuously. That one segment is now collapsed to
   the single-segment wildcard, leaving the rest of the path literal so a read of
   some other secret is still anomalous.

2. Listing the token in `opens` does NOT satisfy R0006 — it is a separate rule
   that must name the process allowed to read a ServiceAccount token. Derived
   from the learned execs' argv[0], because comm is argv[0]'s basename truncated
   by the kernel to 15 chars: /usr/local/bin/argocd-application-controller is
   seen as "argocd-applicat", and the exec PATH basename ("argocd") would never
   match.

Measured on 5 minutes of benign GitOps, per component:

  repo-server   0 false positives — the worst offender before, and the one that
                was missing git/helm/kustomize and the github.com egress
  R0002         down to 1

Remaining and NOT fixed here: R0006 still fires ~22 times per 5 minutes on the
controllers despite processAllowed naming exactly the comm the alert carries
(e.g. alert comm "argocd-server" vs processAllowed ["argocd-server","tini"]).
Ruled out: token path missing from opens (fixed), learned-profile precedence
(deleted the learned profiles, no change), wrong comm (verified identical). So
R0006's processAllowed appears not to be honoured by this node-agent build —
an engine issue rather than SBoB content, tracked separately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
…onoured

R0006 shipped with supportPolicy: false. node-agent gates the allow-list on that
flag (rule_manager.go:225), so rulePolicies.R0006.processAllowed was DEAD
CONFIG — parsed, applied, visible in the profile, silently ignored. A k8s-client
workload therefore fired R0006 on its OWN ServiceAccount token no matter what
the SBoB said.

Note this is NOT profileDependency, which is the neighbouring field and an enum
(0=Required, 1=Optional, 2=NotRequired). R0006 already had 0 = Required, which
is exactly why the wrong conclusions came first: the rule DOES depend on a
profile, it just would not honour the allow-list.

Measured on the same 5-minute benign GitOps run, whole argocd namespace:

  before the re-learn      thousands (reporter)
  after re-learn           22   (21x R0006, 1x R0002)
  after supportPolicy      1    (R0002 on dex, which has no SBoB bound)

Every component with a SBoB bound now produces zero false positives.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
This PR is the example-layout restructure plus the redis-protocol distro
comparison. The Argo CD work (bob#170) landed here only because it happened in
the same session, and it does not belong in the same review.

Reverted to main:
  example/argocd-server-attacks.yaml, example/argocd-repo-server-attacks.yaml
  example/argocd/sbobs/cp-argocd-{server,repo-server,application-controller,
    applicationset-controller}.yaml
  kubescape/default-rules.yaml      (R0006 supportPolicy)
  kubescape/values.yaml             (learn window 2m -> 15m)

Removed (added for the Argo CD work only):
  example/argocd/drive-gitops-workload.sh
  example/argocd/sbobs/cp-argocd-notifications-controller.yaml
  scripts/sbob-from-learned.py

The submodule now points at eeec4a8 rather than ed64bf2, which drops the
AUTOTUNING.md note about supportPolicy (Argo-driven) while keeping the three
fixes the distro comparison actually depends on: the suite-path follow-up, the
--namespace retarget that lets one suite address several deployments, and the
metrics.json write that the comparison reads.

The distro results are unaffected — those tunes ran against the 2m learn window,
not the 15m one reverted here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
The restructure moved postgres-vuln/ under example/. I updated the Dockerfile
path in both Buildx steps but left `context: postgres-vuln`, which no longer
exists — so the image build failed on a directory that had moved out from under
it. Caught by CodeRabbit on #173.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
The KS_RUNC auto-detection was broken in two ways.

It read the local machine's process table to configure node-agent, which
runs on the cluster NODES. Those are the same host only on a single-node
local cluster; on a multi-node or remote cluster it sampled an unrelated
`ps`, and on a client machine with no containerd shim it produced the
malformed `--set global.overrideRuntimePath=]*/bin/runc` reported in #172.
It also ran three subshells (ps, grep, findmnt) on EVERY make invocation
because the assignments were immediate-expansion globals.

KS_RUNC and KS_RUNC_MNT are now plain opt-in variables, validated as
absolute paths, with the validation subshell inside the guard so the
default path spawns none. With both unset the rendered manifest is
byte-identical to before apart from cert regeneration, so CI is untouched.

The filesystem mount moves out of --set and into the post-renderer. It
could not stay: the chart ships nodeAgent.volumes as a fully-populated
list, so --set nodeAgent.volumes[0] overwrites the first default entry and
drops /profiles, while the top-level `volumes` key appends but is global —
it was injecting the mount into all six chart workloads when only
node-agent needs it. Renaming force-network-streaming.sh to post-render.sh
reflects that it now does two rewrites; the node-agent DaemonSet is the
only document round-tripped through the YAML parser so helm's embedded
config.json block scalars are emitted verbatim. The PyYAML import happens
after the opt-in is confirmed, keeping the default path dependency-free.

Refs #172

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
entlein and others added 4 commits August 1, 2026 13:23
eeec4a8 was a mid-branch commit; entlein/bob#59 and #60 have since merged.
The trees are identical, so this is pointer hygiene rather than a content
change — it stops the gitlink referencing a commit that is not on main.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
The postgres leg failed with all 21 detections missed and 0 false
positives — the signature of tuning a container the attacks never touched.

Only one profile ever completed in the window: pg-1-bootstrap-controller.
That is a short-lived init container, so its profile finishes within
seconds while pg-client is still being exercised. The bare `head -1`
fallback in the poll loop accepted it on the FIRST iteration and broke out,
so the tune measured the bootstrap container while every attack landed on
pg-client. main won this race; this branch did not.

The match is now the only thing that ends the wait. The unmatched fallback
moves after the timeout, where it still produces diagnostics but says
loudly that the result is not authoritative.

Also fixes the `grep -v client` filter, which was self-defeating for
postgres: MATCH is pg-client, so the exclusion dropped exactly the profile
being searched for and left the loop relying on the buggy fallback. And
points the path filter at example/postgres/** after the restructure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
--json documented itself as machine-readable but printed the JSON blob
after the full human report, so anything piping stdout into jq had to strip
the preceding text first. The report now goes to stderr when --json is set,
leaving stdout valid JSON on its own; without the flag nothing changes.

Reported by CodeRabbit on #173.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
The replace() was unguarded, so a chart change that renamed or reformatted
the field would turn the rewrite into a silent no-op — which is precisely
the bug this script exists to prevent. The flag renders false, the profile's
network shape stays inert, and R0005/R0011 never fire while the install
looks healthy.

The three cases are now distinguished: the field present as false is
rewritten; already true is passed through untouched (a cloud-submit stack
legitimately renders it that way); absent entirely is a hard error, because
at that point the rewrite is doing nothing and detection would go quietly
dead.

Reported by CodeRabbit on #173.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
@entlein
entlein force-pushed the feat/restructure-release-redis-distros branch from dca5f80 to 06ad7c6 Compare August 1, 2026 11:24
@entlein
entlein merged commit 34acc2d into main Aug 1, 2026
7 checks passed
@entlein
entlein deleted the feat/restructure-release-redis-distros branch August 1, 2026 11:26
entlein added a commit that referenced this pull request Aug 2, 2026
…t-in

Reverts 3bb7081, which dropped the Argo CD changes from #173 to keep that PR
scoped to the restructure. This brings back the five re-learned SBoBs, the
GitOps driver that makes the learn window representative, sbob-from-learned.py,
and the R0006 supportPolicy fix.

Two things are deliberately NOT restored as they were.

kubescape/values.yaml keeps the chart default learningPeriod of 2m. The
reverted commit set it to 15m globally, which would break every CI leg: the
workflow waits 600s for a completed ContainerProfile, so a 15m window means no
profile ever completes and the whole matrix times out. Argo CD genuinely needs
longer than 2m — bob#170 was caused by a 2m window on an idle control plane
learning 5 execs instead of 10 and 51 opens instead of 517 — so the longer
window is now an opt-in Makefile variable, validated as a Go duration and
absent from the helm invocation unless set:

  make kubescape KS_LEARN_PERIOD=15m

The pkg submodule pointer stays at main's 2a2b337. The reverted commit pointed
it at ed64bf2, which lives on an inner topic branch and is not reachable from
inner main — exactly the off-main gitlink CodeRabbit flagged on #173. The
AUTOTUNING.md note on that commit (supportPolicy gating rulePolicies) still
needs landing inner-side on its own.

Refs #170

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jd9m962b1JAHdAy3rGe7nm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant