Skip to content

Commit a330c84

Browse files
committed
Enable rapiDAST
1 parent 4087219 commit a330c84

File tree

2 files changed

+327
-0
lines changed

2 files changed

+327
-0
lines changed

.tekton/rapidast-check.yaml

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
apiVersion: tekton.dev/v1
2+
kind: Task
3+
metadata:
4+
labels:
5+
app.kubernetes.io/version: "0.1"
6+
annotations:
7+
tekton.dev/pipelines.minVersion: "0.12.1"
8+
tekton.dev/tags: "konflux"
9+
name: rapidast-check
10+
spec:
11+
description: >-
12+
This task uses RapiDAST to scan a running application. Intended for use in an Integration Test, not a build pipeline. This task requires a KUBECONFIG_SECRET for an existing cluster/namespace
13+
where the target application is running. To make this application reachable by RapiDAST from outside this environment, `oc port-forward` is used.
14+
results:
15+
- name: TEST_OUTPUT
16+
description: Tekton task test output.
17+
- name: SCAN_OUTPUT
18+
description: RapiDAST scan result.
19+
params:
20+
- name: KUBECONFIG_SECRET
21+
description: The name of a kubeconfig used to to access the test environment.
22+
type: string
23+
- name: RAPIDAST_CONFIG_VALUE
24+
description: The contents of a rapidast config file. Target URLs for scanning should point to localhost and match PORT_FORWARD_TARGETS.
25+
type: string
26+
- name: PORT_FORWARD_TARGETS
27+
description: Scan targets in test environment and arguments for `oc port-forward` commands. Each host:port in RAPIDAST_CONFIG_VALUE requires a respective port-forward target. Multiple values can be separated with a comma.
28+
default: pod/my-pod 5000:5000
29+
type: string
30+
# TODO add a dotenv file argument, so users can pass in .env file with secrets etc used for scanning
31+
volumes:
32+
- name: shared
33+
emptyDir: {}
34+
sidecars:
35+
- name: port-forward
36+
image: quay.io/konflux-ci/konflux-test:latest
37+
volumeMounts:
38+
- name: shared
39+
mountPath: /shared
40+
env:
41+
- name: KUBECONFIG
42+
value: /shared/kubeconfig.yml
43+
- name: KUBECONFIG_VALUE
44+
valueFrom:
45+
secretKeyRef:
46+
name: "$(params.KUBECONFIG_SECRET)"
47+
key: kubeconfig
48+
- name: PORT_FORWARD_TARGETS
49+
value: "$(params.PORT_FORWARD_TARGETS)"
50+
script: |
51+
#!/bin/bash
52+
set -ex
53+
54+
# to share with rapidast image
55+
cp utils.sh /shared
56+
57+
cat <<< "$KUBECONFIG_VALUE" > "$KUBECONFIG"
58+
echo "Wrote kubeconfig for new environment to $KUBECONFIG"
59+
60+
echo "${PORT_FORWARD_TARGETS}"
61+
62+
check_pid_listening_port() {
63+
local pid=$1
64+
# checks that PID has at least one local port open
65+
if ss -tlnp | grep -q "pid=${pid},"; then
66+
return 0
67+
fi
68+
return 1
69+
}
70+
export -f check_pid_listening_port
71+
72+
# split on ",", then parse as json to handle whitespace
73+
IFS="," read -ra TARGETS_ARRAY <<< "${PORT_FORWARD_TARGETS}"
74+
75+
for t in "${TARGETS_ARRAY[@]}"; do
76+
oc port-forward --address 0.0.0.0 ${t} &
77+
PID=$!
78+
timeout 30s bash -c "set -x; until check_pid_listening_port ${PID}; do sleep 2s; done" || {
79+
echo "oc port-foward failed to open local port"
80+
exit 1
81+
}
82+
done
83+
84+
touch /shared/port-forward-ready # signal to rapidast container
85+
sleep infinity
86+
87+
steps:
88+
# perform rapidast scan
89+
- name: rapidast-check
90+
image: quay.io/redhatproductsecurity/rapidast
91+
# TODO add a dotenv file argument, so users can pass in .env file with secrets etc used for scanning
92+
env:
93+
# this can be used by some rapidast generic scanners, like trivy
94+
- name: KUBECONFIG
95+
value: /shared/kubeconfig.yml
96+
volumeMounts:
97+
- name: shared
98+
mountPath: /shared
99+
script: |
100+
#!/bin/bash
101+
set -ex
102+
103+
# wait til sidecar container signals all port-fowarding is ready
104+
timeout 5m bash -c 'until [ -f /shared/port-forward-ready ]; do sleep 2; done' || {
105+
echo "[ERROR] oc port-forward commands in sidecar container not ready in time. Exiting"
106+
exit 1
107+
}
108+
109+
# helper code from konflux-test image, includes handle_error and make_result_json
110+
source /shared/utils.sh
111+
trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT
112+
113+
cat > /shared/rapidast-config.yaml << EOF
114+
$(params.RAPIDAST_CONFIG_VALUE)
115+
EOF
116+
117+
rapidast.py --config /shared/rapidast-config.yaml
118+
119+
orig_sarif_file=$(find results -name rapidast-scan-results.sarif)
120+
cp "${orig_sarif_file}" /shared/rapidast-scan-results.sarif
121+
122+
- name: post-process
123+
image: quay.io/konflux-ci/konflux-test
124+
volumeMounts:
125+
- name: shared
126+
mountPath: /shared
127+
script: |
128+
#!/bin/bash
129+
set -ex
130+
131+
# helper code from konflux-test image, includes handle_error and make_result_json
132+
source /utils.sh
133+
trap 'handle_error $(results.TEST_OUTPUT.path)' EXIT
134+
135+
sarif_file=/shared/rapidast-scan-results.sarif
136+
137+
# Process SCAN_OUTPUT similar to clair scan, but don't use optional 'unpatched_vulnerabilities' field
138+
scan_result='{"vulnerabilities":{"critical":0, "high":0, "medium":0, "low":0, "unknown":0}}'
139+
140+
if [ ! -s "$sarif_file" ]; then
141+
echo "No RapiDAST SARIF file found or it's empty."
142+
note="Task $(context.task.name) failed: No RapiDAST SARIF file found or it's empty. For details, check Tekton task log."
143+
TEST_OUTPUT=$(make_result_json -r "ERROR" -t "$note")
144+
echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)"
145+
exit 0
146+
fi
147+
148+
# SARIF level mapping: 'error' -> critical, 'warning' -> high, 'note' -> medium.
149+
result=$(jq -rce \
150+
'{
151+
vulnerabilities:{
152+
critical: ([.runs[0].results[]? | select(.level == "error")] | length // 0),
153+
high: ([.runs[0].results[]? | select(.level == "warning")] | length // 0),
154+
medium: ([.runs[0].results[]? | select(.level == "note")] | length // 0),
155+
low: ([.runs[0].results[]? | select(.level == "none")] | length // 0),
156+
unknown: ([.runs[0].results[]? | select(.level == "unknown")] | length // 0)
157+
}
158+
}' "$sarif_file")
159+
160+
scan_result=$(jq -s -rce \
161+
'.[0].vulnerabilities.critical += .[1].vulnerabilities.critical |
162+
.[0].vulnerabilities.high += .[1].vulnerabilities.high |
163+
.[0].vulnerabilities.medium += .[1].vulnerabilities.medium |
164+
.[0].vulnerabilities.low += .[1].vulnerabilities.low |
165+
.[0].vulnerabilities.unknown += .[1].vulnerabilities.unknown |
166+
.[0]' <<<"$scan_result $result")
167+
168+
echo "$scan_result" | tee "$(results.SCAN_OUTPUT.path)"
169+
170+
note="Task $(context.task.name) completed: Refer to Tekton task result SCAN_OUTPUT for vulnerabilities scanned by RapiDAST."
171+
TEST_OUTPUT=$(make_result_json -r "SUCCESS" -t "$note")
172+
echo "${TEST_OUTPUT}" | tee "$(results.TEST_OUTPUT.path)"
173+
174+
# TODO push SARIF result file to quay.io with oras, much like sast tasks

.tekton/rapidast.yaml

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# An example Pipeline to use in an IntegrationTest, this is for a component named "test-component"
2+
# which would be built an a separate build pipeline
3+
apiVersion: tekton.dev/v1
4+
kind: Pipeline
5+
metadata:
6+
name: rapidast-integration-test
7+
spec:
8+
params:
9+
# this SNAPSHOT should be produced by the build pipeline and contain the containerImage we want to test with rapidast
10+
- name: SNAPSHOT
11+
type: string
12+
tasks:
13+
# we need an clean environment where we have permission to deploy k8s resources
14+
# this env will only provide namespace-admin, not cluster-admin. For components
15+
# that require a cluster-admin, an ephemeral EaaS cluster is needed
16+
- name: provision-env
17+
taskRef:
18+
params:
19+
- name: name
20+
value: eaas-provision-space
21+
- name: bundle
22+
value: quay.io/konflux-ci/tekton-catalog/task-eaas-provision-space:0.1@sha256:8a344ed61dfeabf741f3f08892414f223ade1849f995da0db172e79c4afc21a3
23+
- name: kind
24+
value: task
25+
resolver: bundles
26+
params:
27+
- name: ownerName
28+
value: $(context.pipelineRun.name)
29+
- name: ownerUid
30+
value: $(context.pipelineRun.uid)
31+
32+
# use image from snapshot in new deployment in fresh environment
33+
- name: deploy-app
34+
params:
35+
- name: SNAPSHOT
36+
value: "$(params.SNAPSHOT)"
37+
runAfter:
38+
- provision-env
39+
taskSpec:
40+
params:
41+
- name: SNAPSHOT
42+
type: string
43+
volumes:
44+
- name: credentials
45+
emptyDir: {}
46+
steps:
47+
# write the kubeconfig to a volume we can use in subsequent steps
48+
# and emptyDir volume will exist for the lifetime of a single taskRun
49+
- name: get-kubeconfig
50+
image: quay.io/konflux-ci/konflux-test:latest
51+
env:
52+
- name: KUBECONFIG
53+
value: /credentials/kubeconfig.yml
54+
- name: KUBECONFIG_VALUE
55+
valueFrom:
56+
secretKeyRef:
57+
name: "$(tasks.provision-env.results.secretRef)"
58+
key: kubeconfig
59+
volumeMounts:
60+
- name: credentials
61+
mountPath: /credentials
62+
script: |
63+
#!/bin/bash
64+
set -euxo pipefail
65+
66+
cat <<< "$KUBECONFIG_VALUE" > "$KUBECONFIG"
67+
echo "Wrote kubeconfig for new environment to $KUBECONFIG"
68+
69+
# we need to copy the pull secret into our fresh test environment
70+
# for the SNAPSHOT image stored in a private quay repo
71+
- name: copy-pull-secret
72+
image: quay.io/konflux-ci/konflux-test:latest
73+
env:
74+
- name: DEST_KUBECONFIG
75+
value: /credentials/kubeconfig.yml
76+
# used to pull SNAPSHOT image, should already exist in user namespace
77+
- name: PULL_SECRET
78+
value: multiarch-tuning-operator-pull
79+
volumeMounts:
80+
- name: credentials
81+
mountPath: /credentials
82+
script: |
83+
#!/bin/bash
84+
set -euxo pipefail
85+
86+
oc get secrets $PULL_SECRET -o json | jq 'del(.metadata.creationTimestamp,
87+
.metadata.uid, .metadata.resourceVersion,
88+
.metadata.annotations."kubectl.kubernetes.io/last-applied-configuration",
89+
.metadata.namespace)' > /tmp/pull-secret.json
90+
oc --kubeconfig=$DEST_KUBECONFIG apply -f /tmp/pull-secret.json
91+
oc --kubeconfig=$DEST_KUBECONFIG secret link --for=pull default $PULL_SECRET
92+
93+
# deploy an instance of our app using the snapshot image in our test env
94+
- name: deploy-app
95+
image: quay.io/konflux-ci/konflux-test:latest
96+
env:
97+
- name: SNAPSHOT
98+
value: "$(params.SNAPSHOT)"
99+
- name: KUBECONFIG
100+
value: /credentials/kubeconfig.yml
101+
volumeMounts:
102+
- name: credentials
103+
mountPath: /credentials
104+
script: |
105+
#!/bin/bash
106+
set -euxo pipefail
107+
108+
IMAGE=$(echo "$SNAPSHOT" | jq -r '.components[0].containerImage')
109+
echo "Extracted image: $IMAGE"
110+
111+
oc create deployment multiarch-tuning-operator --image=$IMAGE
112+
oc wait --for=condition=Available=true deployment/multiarch-tuning-operator --timeout=120s
113+
114+
- name: rapidast-check
115+
runAfter:
116+
- deploy-app
117+
taskRef:
118+
resolver: git
119+
params:
120+
- name: url
121+
value: https://github.com/redhatproductsecurity/rapidast
122+
# TODO change revision after merge
123+
- name: revision
124+
value: konflux-example
125+
- name: pathInRepo
126+
value: .tekton/rapidast-check.yaml
127+
params:
128+
- name: KUBECONFIG_SECRET
129+
value: "$(tasks.provision-env.results.secretRef)"
130+
# this is necessary to make the target app in the remote env accessible to rapidast
131+
- name: PORT_FORWARD_TARGETS
132+
value: "deployment/multiarch-tuning-operator 5000:5000"
133+
# all target URLs in rapidast config should be localhost, and port-forwarded
134+
- name: RAPIDAST_CONFIG_VALUE
135+
value: |
136+
config:
137+
configVersion: 6
138+
results:
139+
exclusions:
140+
rules:
141+
- name: "Exclude findings below a severity level of Important"
142+
cel_expression: ".result.level != 'error' && .result.level != 'warning'"
143+
144+
application:
145+
shortName: "multiarch-tuning-operator"
146+
url: "http://127.0.0.1:5000" # root URL of the application, should always point to localhost
147+
148+
scanners:
149+
zap:
150+
spider: {}
151+
passiveScan: {}
152+
activeScan:
153+
policy: API-scan-minimal

0 commit comments

Comments
 (0)