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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/rapidast/bpfapplication-oobtkube-cr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
apiVersion: bpfman.io/v1alpha1
kind: BpfApplication
metadata:
name: rapidast-oobtkube-test
namespace: bpfman
labels:
app.kubernetes.io/name: rapidast-oobtkube-test
app.kubernetes.io/component: dast-scan
annotations:
description: "Enriched BpfApplication CR for oobtkube blind command injection testing"
spec:
nodeSelector:
matchLabels:
kubernetes.io/os: linux
matchExpressions:
- key: node-role.kubernetes.io/worker
operator: Exists
byteCode:
image:
url: "quay.io/bpfman-bytecode/xdp_pass:latest"
imagePullPolicy: IfNotPresent
imagePullSecret:
name: "bpfman-registry-secret"
namespace: "bpfman"
mapOwnerSelector:
matchLabels:
bpfman.io/ownedByApp: rapidast-oobtkube-test
matchExpressions:
- key: bpfman.io/mapOwner
operator: In
values:
- "rapidast-test-maps"
- "shared-maps"
programs:
- name: xdp_pass_func
type: XDP
xdp:
links:
- interfaceSelector:
interfaces:
- "eth0"
- "net1"
priority: 50
proceedOn:
- Pass
- DispatcherReturn
- name: tc_ingress_func
type: TC
tc:
links:
- direction: Ingress
interfaceSelector:
interfaces:
- "eth0"
networkNamespaces:
pods:
matchLabels:
app: bpfman-agent
priority: 100
proceedOn:
- Pipe
- DispatcherReturn
- name: uprobe_ssl_func
type: UProbe
uprobe:
links:
- target: "/usr/lib/x86_64-linux-gnu/libssl.so.3"
containers:
containerNames:
- "bpfman-agent"
pods:
matchLabels:
app: bpfman-agent
17 changes: 17 additions & 0 deletions .github/rapidast/oobtkube-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
config:
configVersion: 6
base_results_dir: /opt/rapidast/results

application:
shortName: "bpfman-operator-oobtkube"
url: "https://localhost"

general:
container:
type: none

scanners:
generic_oobtkube:
results: "/opt/rapidast/results/oobtkube-results.sarif"
inline: |
python3.12 oobtkube.py --find-all --log-level debug -d 300 -p 6000 -i $POD_IP -f /tmp/bpfapplication-cr.yaml -o /opt/rapidast/results/oobtkube-results.sarif
36 changes: 36 additions & 0 deletions .github/rapidast/zap-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
config:
configVersion: 6
base_results_dir: /opt/rapidast/results
results:
exclusions:
rules:
- name: "Exclude findings below Important"
cel_expression: ".result.level != 'error' && .result.level != 'warning'"

application:
shortName: "bpfman-operator-metrics"
url: "https://127.0.0.1:8443"

general:
authentication:
type: http_header
parameters:
name: Authorization
value_from_var: AUTH_TOKEN
container:
type: none

scanners:
zap:
spider:
maxDuration: 2
url: "https://127.0.0.1:8443/metrics"
passiveScan:
disabledRules: "2,10015,10024,10027,10054,10096,10109,10112"
activeScan:
policy: "API-scan-minimal"
report:
format: ["json", "html", "sarif"]
miscOptions:
enableUI: false
updateAddons: false
179 changes: 179 additions & 0 deletions .github/workflows/rapidast.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
name: RapiDAST DAST Scan

on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 0" # Sunday midnight UTC

jobs:
rapidast-dast-scan:
name: RapiDAST Security Scan
runs-on: ubuntu-24.04
env:
BPFMAN_AGENT_IMG: quay.io/bpfman/bpfman-agent:int-test
BPFMAN_OPERATOR_IMG: quay.io/bpfman/bpfman-operator:int-test
steps:
Comment on lines +8 to +15

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Define explicit least-privilege GITHUB_TOKEN permissions.

No permissions block is set, so token scopes default broader than necessary for this job.

Suggested hardening
 name: RapiDAST DAST Scan
 
 on:
   workflow_dispatch:
   schedule:
     - cron: "0 0 * * 0" # Sunday midnight UTC
 
+permissions:
+  contents: read
+
 jobs:
   rapidast-dast-scan:
     name: RapiDAST Security Scan

As per coding guidelines, .github/workflows/**/*: "Least privilege: minimize GITHUB_TOKEN permissions".

🤖 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 @.github/workflows/rapidast.yml around lines 8 - 15, The rapidast-dast-scan
job lacks an explicit permissions block, causing the GITHUB_TOKEN to have
default scopes that are broader than necessary. Add a permissions block at the
job level (as a sibling to the name, runs-on, env, and steps fields) that
explicitly defines only the minimal required permissions needed for the RapiDAST
Security Scan job to run. This ensures the GITHUB_TOKEN follows the principle of
least privilege by restricting access to only what is strictly necessary for
this workflow to function.

Source: Coding guidelines

- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
sudo rm -rf /usr/local/lib/android

- name: Checkout bpfman-operator
uses: actions/checkout@v6
with:
Comment on lines +25 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -type f -name "rapidast.yml" .github/

Repository: openshift/bpfman-operator

Length of output: 240


🏁 Script executed:

fd -t f -name "rapidast.yml" .github/

Repository: openshift/bpfman-operator

Length of output: 300


🏁 Script executed:

fd "rapidast.yml" .github/

Repository: openshift/bpfman-operator

Length of output: 100


🏁 Script executed:

cat -n .github/workflows/rapidast.yml | head -150

Repository: openshift/bpfman-operator

Length of output: 6164


🏁 Script executed:

cat -n .github/workflows/rapidast.yml | tail -20

Repository: openshift/bpfman-operator

Length of output: 920


🏁 Script executed:

rg 'uses:' .github/workflows/rapidast.yml

Repository: openshift/bpfman-operator

Length of output: 281


Pin all GitHub Actions to full commit SHAs.

Lines 25, 30, 36, 44, 133, and 141 use mutable tags (@v5, @v6), which breaks immutability guarantees for the CI supply chain. Actions with mutable tags can be re-released with breaking or malicious changes without explicit consent.

Replace all action tags with full commit SHAs:

  • actions/checkout@v6actions/checkout@eace6... (current v6 SHA)
  • actions/setup-go@v6actions/setup-go@0c52d... (current v6 SHA)
  • actions/cache@v5actions/cache@0c45773... (current v5 SHA)
  • actions/upload-artifact@v6actions/upload-artifact@83fd05... (current v6 SHA)
🧰 Tools
🪛 zizmor (1.25.2)

[error] 25-25: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 @.github/workflows/rapidast.yml around lines 25 - 26, Replace all mutable
GitHub Actions version tags with their corresponding full commit SHAs in the
rapidast.yml workflow file to ensure supply chain immutability. Update
actions/checkout from `@v6`, actions/setup-go from `@v6`, actions/cache from `@v5`,
and actions/upload-artifact from `@v6` by replacing each mutable tag with the full
commit SHA provided in the comment. This prevents these actions from being
re-released with breaking or malicious changes without explicit consent.

Sources: Coding guidelines, Linters/SAST tools

fetch-depth: 0

Comment on lines +24 to +28

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Disable checkout credential persistence.

actions/checkout currently persists credentials in local git config. This is avoidable token exposure in a multi-step job.

Suggested hardening
       - name: Checkout bpfman-operator
         uses: actions/checkout@v6
         with:
           fetch-depth: 0
+          persist-credentials: false

As per coding guidelines, .github/workflows/**/*: "Least privilege: minimize GITHUB_TOKEN permissions".

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 24-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 25-25: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 @.github/workflows/rapidast.yml around lines 24 - 28, The actions/checkout@v6
action in the "Checkout bpfman-operator" step is currently persisting
credentials in the local git config, which creates unnecessary token exposure.
Add the persist-credentials parameter set to false in the with block of the
checkout action to disable credential persistence and comply with the least
privilege principle for GITHUB_TOKEN permissions.

Sources: Coding guidelines, Linters/SAST tools

- name: Install Go
uses: actions/setup-go@v6
with:
go-version: "1.25"
cache: false

- name: Restore Go module cache
uses: actions/cache@v5
with:
path: ~/go/pkg/mod
key: go-mod-${{ hashFiles('**/go.sum') }}
restore-keys: |
go-mod-

- name: Restore Go build cache
uses: actions/cache@v5
with:
path: ~/.cache/go-build
key: go-build-${{ github.sha }}
restore-keys: |
go-build-

- name: Build operator and agent images
run: make build-images

- name: Deploy operator on Kind cluster
run: make run-on-kind

- name: Wait for operator pods to be ready
run: |
echo "Waiting for bpfman-operator deployment..."
kubectl rollout status deployment/bpfman-operator -n bpfman --timeout=120s
echo "Waiting for bpfman-daemon daemonset..."
kubectl rollout status daemonset/bpfman-daemon -n bpfman --timeout=120s
echo "All pods ready:"
kubectl get pods -n bpfman

# --- ZAP Scan ---

- name: Port-forward metrics service
run: |
kubectl port-forward svc/controller-manager-metrics-service 8443:8443 -n bpfman &
echo $! > /tmp/port-forward.pid
sleep 3
echo "Port-forward PID: $(cat /tmp/port-forward.pid)"

- name: Create service account token for auth
id: sa-token
run: |
TOKEN=$(kubectl create token default -n bpfman --duration=30m)
echo "::add-mask::${TOKEN}"
echo "AUTH_TOKEN=Bearer ${TOKEN}" >> "$GITHUB_ENV"

- name: Run RapiDAST ZAP scan
continue-on-error: true
run: |
mkdir -p /tmp/rapidast-results/zap
docker run --rm \
--network host \
-v "${{ github.workspace }}/.github/rapidast/zap-config.yaml:/opt/rapidast/config.yaml:ro" \
-v "/tmp/rapidast-results/zap:/opt/rapidast/results" \
-e "AUTH_TOKEN=${AUTH_TOKEN}" \
quay.io/redhatproductsecurity/rapidast:development \
rapidast.py --config /opt/rapidast/config.yaml

- name: Stop port-forward
if: always()
run: |
if [ -f /tmp/port-forward.pid ]; then
kill "$(cat /tmp/port-forward.pid)" 2>/dev/null || true
fi

# --- oobtkube Scan ---

- name: Apply enriched BpfApplication CR
continue-on-error: true
run: |
kubectl apply -f .github/rapidast/bpfapplication-oobtkube-cr.yaml

- name: Get operator pod IP for oobtkube
id: pod-ip
run: |
POD_NAME=$(kubectl get pods -n bpfman -l control-plane=controller-manager -o jsonpath='{.items[0].metadata.name}')
POD_IP=$(kubectl get pod "${POD_NAME}" -n bpfman -o jsonpath='{.status.podIP}')
echo "POD_IP=${POD_IP}" >> "$GITHUB_ENV"
echo "Operator pod: ${POD_NAME} at ${POD_IP}"

- name: Run RapiDAST oobtkube scan
continue-on-error: true
run: |
mkdir -p /tmp/rapidast-results/oobtkube
docker run --rm \
--network host \
-v "${{ github.workspace }}/.github/rapidast/oobtkube-config.yaml:/opt/rapidast/config.yaml:ro" \
-v "${{ github.workspace }}/.github/rapidast/bpfapplication-oobtkube-cr.yaml:/tmp/bpfapplication-cr.yaml:ro" \
-v "/tmp/rapidast-results/oobtkube:/opt/rapidast/results" \
-e "POD_IP=${POD_IP}" \
quay.io/redhatproductsecurity/rapidast:development \
rapidast.py --config /opt/rapidast/config.yaml

# --- Results ---

- name: Upload ZAP scan results
if: always()
uses: actions/upload-artifact@v6
with:
name: rapidast-zap-results
path: /tmp/rapidast-results/zap/
if-no-files-found: warn

- name: Upload oobtkube scan results
if: always()
uses: actions/upload-artifact@v6
with:
name: rapidast-oobtkube-results
path: /tmp/rapidast-results/oobtkube/
if-no-files-found: warn

Comment on lines +131 to +146

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Sign scan artifacts before upload (Sigstore/cosign).

Artifacts are uploaded unsigned, so downstream consumers cannot verify provenance/integrity.

As per coding guidelines, .github/workflows/**/*: "Sign artifacts with Sigstore/cosign".

🧰 Tools
🪛 zizmor (1.25.2)

[error] 133-133: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 141-141: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 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 @.github/workflows/rapidast.yml around lines 131 - 146, The "Upload ZAP scan
results" and "Upload oobtkube scan results" steps are uploading artifacts
without signing them, making it impossible for downstream consumers to verify
provenance and integrity. Add a signing step before each upload-artifact action
that uses Sigstore/cosign to sign the artifacts from /tmp/rapidast-results/zap/
and /tmp/rapidast-results/oobtkube/ respectively, then configure the
upload-artifact actions to include the generated signatures. This ensures
artifacts can be verified by consumers.

Source: Coding guidelines

- name: Generate scan summary
if: always()
run: |
ZAP_REPORT="/tmp/rapidast-results/zap/bpfman-operator-metrics/zap/zap-report.json"
OOBT_REPORT="/tmp/rapidast-results/oobtkube/oobtkube-results.sarif"

if [ -f "$ZAP_REPORT" ]; then
ZAP_ALERTS=$(jq '.site[].alerts | length' "$ZAP_REPORT" 2>/dev/null || echo "N/A")
ZAP_LINE="- **Alerts found**: ${ZAP_ALERTS}
- Full report available in workflow artifacts"
else
ZAP_LINE="- No ZAP report found (scan may have failed or produced no results)"
fi

if [ -f "$OOBT_REPORT" ]; then
OOBT_RESULTS=$(jq '.runs[0].results | length' "$OOBT_REPORT" 2>/dev/null || echo "N/A")
OOBT_LINE="- **Injection detections**: ${OOBT_RESULTS}"
else
OOBT_LINE="- No oobtkube report found (scan may have failed or produced no results)"
fi

cat >> "$GITHUB_STEP_SUMMARY" <<EOF
## RapiDAST DAST Scan Results

### ZAP Scan (metrics endpoint)
${ZAP_LINE}

### oobtkube Scan (BpfApplication CRD injection)
${OOBT_LINE}

---
*Scan powered by [RapiDAST](https://github.com/RedHatProductSecurity/rapidast) \`development\` image*
EOF