Local Scanner and Hook deployment
If you are integrating a new scanner or hook and want to test from a local build, this document will guide you through it. For simplicity's sake, this guide is written only for local cluster setups using kind. Other setups (e.g., minikube, Docker Desktop's integrated Kubernetes) are possible but might require extra setup and tweaking efforts to run with our task files. We also assume that you are or have followed the steps in either the Integrating A Scanner or Integrating A Hook guide.
Kind Cluster Setupâ
Easiest way to setup a kind cluster for running secureCodeBox for local development is to use the task task prepare-testing-env
command.
This will start a kind cluster with the name testing-env and build the operator and deploy it to the cluster.
IF you intent to use it to run the integration tests for a scanner or hook you can skip this as the test:integration tasks will automatically set this up too, if it isn't already running.
Taskfile-based build & deploy (recommended)â
To make local testing easier, the secureCodeBox team has provided a Taskfile based solution. The specific Task targets and examples to customize them are given in these documents:
This document explains how to use these targets to deploy your scanner locally.
Scanner only:
-
Inspect your scanner's Taskfile.yaml. If your scanner uses a remote Docker image (such as one hosted on Dockerhub), the
hasCustomScanner
variable will automatically be set to false. If you have defined your own Dockerfile in thescanner/
directory, you should leave the line as-is. -
In the root of the secureCodeBox git repository, under
hook-sdk/nodejs/
andparser-sdk/nodejs/
, you need to build the Dockerfiles. This ensures that you have the latest version of the SDK available locally. You need this in order to build secureCodeBox parsers and hooks. To build the image:Kind: run
task build
. -
In your scanner or hook directory, build the Dockerfiles:
Kind: run
task build
. -
Run
task deploy
to install your Helm chart in your active Kubernetes cluster into theintegration-tests
namespace. The task ensures that the image name and tag matches that built in the previous step. -
Now run an example scan and inspect whether the images are correctly used.
Example shellâ
securecodebox$ task prepare-testing-env
task: [create-kind-cluster] echo "Starting kind cluster for testing environment"
Starting kind cluster for testing environment
task: [create-kind-cluster] kind create cluster --name testing-env
Creating cluster "testing-env" ...
â Ensuring node image (kindest/node:v1.33.1) đŧ
â Preparing nodes đĻ
â Writing configuration đ
â Starting control-plane đšī¸
â Installing CNI đ
â Installing StorageClass đž
Set kubectl context to "kind-testing-env"
You can now use your cluster with:
kubectl cluster-info --context kind-testing-env
Have a nice day! đ
task: [build-lurker-image] echo "Building lurker image with tag ${IMG_TAG}"
task: [build-operator-image] echo "Building operator image with tag ${IMG_TAG}"
Building lurker image with tag sha-a2d8ce1aa
task: [build-lurker-image] docker build -t ${IMG_NS}/lurker:${IMG_TAG} /src/secureCodeBox/lurker
Building operator image with tag sha-a2d8ce1aa
task: [build-operator-image] docker build -t ${IMG_NS}/operator:${IMG_TAG} /src/secureCodeBox/operator
[+] Building 1.0s (4/4) docker:desktop-linux
[+] Building 1.0s (3/3) docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 966B 0.0s
[+] Building 1.1s (20/20) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 966B 0.0s
=> [internal] load metadata for gcr.io/distroless/static:nonroot 0.4s
=> [internal] load metadata for docker.io/library/golang:1.24.2 1.0s
=> [auth] library/golang:pull token for registry-1.docker.io 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 291B 0.0s
=> [builder 1/11] FROM docker.io/library/golang:1.24.2@sha256:30baaea08c5d1e858329c50f29fe381e9b7d7bced11a0f5f1f69a1504cdfbf5 0.0s
=> [stage-1 1/3] FROM gcr.io/distroless/static:nonroot@sha256:627d6c5a23ad24e6bdff827f16c7b60e0289029b0c79e9f7ccd54ae3279fb45f 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 2.91kB 0.0s
=> CACHED [builder 2/11] WORKDIR /workspace 0.0s
=> CACHED [builder 3/11] COPY go.mod go.mod 0.0s
=> CACHED [builder 4/11] COPY go.sum go.sum 0.0s
=> CACHED [builder 5/11] RUN go mod download 0.0s
=> CACHED [builder 6/11] COPY main.go main.go 0.0s
=> CACHED [builder 7/11] COPY apis/ apis/ 0.0s
=> CACHED [builder 8/11] COPY controllers/ controllers/ 0.0s
=> CACHED [builder 9/11] COPY internal/ internal/ 0.0s
=> CACHED [builder 10/11] COPY utils/ utils/ 0.0s
=> CACHED [builder 11/11] RUN CGO_ENABLED=0 go build -a -o manager main.go 0.0s
=> CACHED [stage-1 2/3] COPY --from=builder /workspace/manager . 0.0s
=> exporting to image 0.0s
[+] Building 1.1s (15/15) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 820B 0.0s
=> [internal] load metadata for docker.io/library/golang:1.24.2 1.0s
=> [internal] load metadata for gcr.io/distroless/static:nonroot 0.4s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 171B 0.0s
=> [builder 1/11] FROM docker.io/library/golang:1.24.2@sha256:30baaea08c5d1e858329c50f29fe381e9b7d7bced11a0f5f1f69a1504cdfbf5 0.0s
=> [stage-1 1/3] FROM gcr.io/distroless/static:nonroot@sha256:627d6c5a23ad24e6bdff827f16c7b60e0289029b0c79e9f7ccd54ae3279fb45f 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 81B 0.0s
=> CACHED [builder 2/11] WORKDIR /workspace 0.0s
=> CACHED [builder 3/7] COPY go.mod go.mod 0.0s
=> CACHED [builder 4/7] COPY go.sum go.sum 0.0s
=> CACHED [builder 5/7] RUN go mod download 0.0s
=> CACHED [builder 6/7] COPY main.go main.go 0.0s
=> CACHED [builder 7/7] RUN CGO_ENABLED=0 go build -a -o lurker main.go 0.0s
=> CACHED [stage-1 2/3] COPY --from=builder /workspace/lurker . 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:5a13c7c870b49203029c1aa85d273e5425ad3e28ca57167c03e822d665db576a 0.0s
=> => naming to docker.io/securecodebox/lurker:sha-a2d8ce1aa 0.0s
View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/7zgy8kalere960epa2fpujgb8
What's next:
View a summary of image vulnerabilities and recommendations â docker scout quickview
What's next:
View a summary of image vulnerabilities and recommendations â docker scout quickview
task: [load-operator-image] kind load docker-image ${IMG_NS}/operator:${IMG_TAG} --name testing-env
task: [load-lurker-image] kind load docker-image ${IMG_NS}/lurker:${IMG_TAG} --name testing-env
Image: "securecodebox/lurker:sha-a2d8ce1aa" with ID "sha256:5a13c7c870b49203029c1aa85d273e5425ad3e28ca57167c03e822d665db576a" not yet present on node "testing-env-control-plane", loading...
Image: "securecodebox/operator:sha-a2d8ce1aa" with ID "sha256:d72d1321720f9df4b6ead554c42f09e382ea8be9652a5bcd500cf051e3ca3981" not yet present on node "testing-env-control-plane", loading...
task: [deploy-operator] echo "Deploying secureCodeBox operator to the testing environment"
Deploying secureCodeBox operator to the testing environment
task: [deploy-operator] kubectl config use-context kind-testing-env
Switched to context "kind-testing-env".
task: [deploy-operator] kubectl create namespace integration-tests || true
namespace/integration-tests created
task: [deploy-operator] helm -n securecodebox-system upgrade --create-namespace --install securecodebox-operator /src/secureCodeBox/operator --wait \
--set="image.repository=docker.io/${IMG_NS}/operator" \
--set="image.tag=${IMG_TAG}" \
--set="image.pullPolicy=IfNotPresent" \
--set="lurker.image.repository=docker.io/${IMG_NS}/lurker" \
--set="lurker.image.tag=${IMG_TAG}" \
--set="lurker.image.pullPolicy=IfNotPresent"
Release "securecodebox-operator" does not exist. Installing it now.
NAME: securecodebox-operator
LAST DEPLOYED: Fri Jul 11 10:03:12 2025
NAMESPACE: securecodebox-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
secureCodeBox Operator Deployed đ
The operator can orchestrate the execution of various security scanning tools inside of your cluster.
You can find a list of all officially supported scanners here: https://www.securecodebox.io/
The website also lists other integrations, like persisting scan results to DefectDojo or Elasticsearch.
The operator send out regular telemetry pings to a central service.
This lets us, the secureCodeBox team, get a grasp on how much the secureCodeBox is used.
The submitted data is chosen to be as anonymous as possible.
You can find a complete report of the data submitted and links to the source-code at: https://www.securecodebox.io/docs/telemetry
The first ping is send one hour after the install, you can prevent this by upgrading the chart and setting `telemetryEnabled` to `false`.
Notice that the Task automatically feeds the chart's AppVersion
into the scannerVersion
build arg.
Manual build & deployâ
Scanner & Parserâ
- Build your scanner image
securecodebox/scanners/your-custom-scanner/scanner$ docker build \
--build-arg=scannerVersion="7.91-r0" \
-t your-custom-scanner:local-dev \
.
- Kind only: import your scanner image
securecodebox$ kind load docker-image your-custom-scanner:local-dev
- Build Parser SDK
securecodebox/parser-sdk/nodejs$ docker build -t securecodebox/parser-sdk-nodejs:local-dev .
- Build your parser image
securecodebox/scanners/your-custom-scanner/parser$ docker build \
--build-arg=baseImageTag="local-dev" \
--build-arg=namespace=securecodebox \
-t your-custom-parser:local-dev \
.
- Kind only: import your parser image
securecodebox$ kind load docker-image your-custom-parser:local-dev
- Update deployment image references.
Change the fields in
values.yaml
file like this:
parser:
image:
repository: your-custom-parser
pullPolicy: Never
tag: local-dev
scanner:
image:
repository: your-custom-scanner
pullPolicy: Never
tag: local-dev
Hookâ
- Build Hook SDK
securecodebox/hook-sdk/nodejs$ docker build -t securecodebox/hook-sdk-nodejs:local-dev .
- Build hook
securecodebox/hooks/your-custom-hook/hook/$ docker build \
--build-arg=baseImageTag="local-dev" \
--build-arg=namespace=securecodebox \
-t your-custom-hook:local-dev \
.
- Kind only: import your hook image
securecodebox$ kind load docker-image your-custom-hook:local-dev
- Update deployment image references.
Change the fields in
values.yaml
file like this:
hook:
image:
repository: your-custom-hook
tag: local-dev
pullPolicy: Never
- Deploy the scanner.
securecodebox/hooks/your-custom-hook/$ helm upgrade --install your-custom-hook .
Debuggingâ
ImagePullBackOff
â
Kubernetes is reporting that it isn't able to find the specified image.
Check with kubectl describe pod [name of pod]
which image your scanner wants to use and whether you have made that image available.
Check your Docker build logs to verify that the image has been correctly tagged.
You can also check if the image is actually available:
- Kind:
docker exec kind-control-plane crictl images
Don't forget that all images used in your Kubernetes cluster must be either remotely accessible or locally available within the cluster.
- Kind: imported after building
- Using Taskfile:
task build
. - Manually:
kind load docker-image parser-nmap:[tag]
.
- Using Taskfile:
Namespaceâ
In some cases, you might accidentally install secureCodeBox in an unexpected namespace. Check that you don't have any left-over releases installed.
$ helm list --all-namespaces
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
nmap integration-tests 1 2021-11-18 15:00:14.712583292 +0100 CET deployed nmap-v3.1.0-alpha1 7.91-r0
securecodebox-operator securecodebox-system 1 2021-11-18 10:09:24.804277463 +0100 CET deployed operator-v3.1.0-alpha1
update-category integration-tests 1 2021-11-18 11:18:45.104860436 +0100 CET deployed update-field-hook-v3.1.0-alpha1
update-severity integration-tests 1 2021-11-18 11:18:45.267164462 +0100 CET deployed update-field-hook-v3.1.0-alpha1
You can install secureCodeBox components in any namespace, however verify that you are starting your scans in the same namespace as where you deployed your scanner or hook.
$ kubectl apply -f ./nmap-scan.yaml -n integration-tests
scan.execution.securecodebox.io/nmap created
$ kubectl get scans -A
NAMESPACE NAME TYPE STATE FINDINGS
integration-tests nmap nmap Done 1