harbor-scanner is a small Go CLI for working with Harbor image scans.
It helps with two common cases:
- an image already exists in Harbor and you want to scan it
- an image exists locally and you want to push it to Harbor, scan it, and save the report
It does not scan images by itself. Harbor and Trivy do that part. This tool just automates the workflow around them.
The CLI can:
- publish an image to Harbor
- trigger a scan
- wait for the scan to finish
- fetch the vulnerability report
- print a severity summary
- save the full report as JSON
You need:
- a running Harbor instance
- Harbor configured with Trivy or another compatible scanner
- a Harbor user or robot account with permission to push images and trigger scans
- a local container CLI if you want to publish images
Supported local CLIs:
dockerpodmannerdctl
Set these environment variables:
export HARBOR_URL=http://harbor.local
export HARBOR_USER=admin
export HARBOR_PASSWORD=your-password-or-robot-tokenOptional:
export HARBOR_REGISTRY=harbor-registry.local
export CONTAINER_CLI=podmanNotes:
HARBOR_URLis the Harbor API base URL.HARBOR_REGISTRYis optional. Use it when the registry hostname is different from the Harbor API hostname.CONTAINER_CLIis optional. It defaults todocker.
Before publishing images, log your container CLI into Harbor:
docker login harbor.localIf you use Podman:
podman login harbor.localIf Harbor is running over plain HTTP, your container runtime must be configured to trust that registry.
go build -o harbor-scanner./harbor-scanner health./harbor-scanner scan-wait library demo-nginx 1.21 reports/demo-nginx-1.21.jsonThis:
- triggers the scan
- waits for it to finish
- prints the summary
- writes the full JSON report to
reports/demo-nginx-1.21.json
./harbor-scanner publish-scan nginx:1.21 library demo-nginx 1.21 reports/demo-nginx-1.21.jsonThis is the main end-to-end workflow.
export CONTAINER_CLI=podman
./harbor-scanner publish-scan nginx:1.21 library demo-nginx 1.21 reports/demo-nginx-1.21.jsonYou can do the same with nerdctl:
export CONTAINER_CLI=nerdctl
./harbor-scanner publish-scan nginx:1.21 library demo-nginx 1.21 reports/demo-nginx-1.21.jsonChecks whether Harbor is reachable.
./harbor-scanner healthPushes a local or remote image into Harbor.
./harbor-scanner publish <source-image> <project> <repository> <tag>Example:
./harbor-scanner publish nginx:1.21 library demo-nginx 1.21Internally this does the equivalent of:
docker pull nginx:1.21
docker tag nginx:1.21 harbor.local/library/demo-nginx:1.21
docker push harbor.local/library/demo-nginx:1.21Except it uses whatever is set in CONTAINER_CLI.
Starts a scan for an image that already exists in Harbor.
./harbor-scanner scan <project> <repository> <tag-or-digest>Example:
./harbor-scanner scan library demo-nginx 1.21Fetches and prints an existing vulnerability report.
./harbor-scanner report <project> <repository> <tag-or-digest>Example:
./harbor-scanner report library demo-nginx 1.21Triggers a scan, waits for completion, and writes a JSON report.
./harbor-scanner scan-wait <project> <repository> <tag-or-digest> <output-file.json>Example:
./harbor-scanner scan-wait library demo-nginx 1.21 reports/demo-nginx-1.21.jsonPublishes an image, scans it, waits for completion, and writes a JSON report.
./harbor-scanner publish-scan <source-image> <project> <repository> <tag> <output-file.json>Example:
./harbor-scanner publish-scan nginx:1.21 library demo-nginx 1.21 reports/demo-nginx-1.21.jsonFor a locally built app:
docker build -t my-api:dev .
./harbor-scanner publish-scan my-api:dev library my-api dev reports/my-api-dev.jsonA Harbor image reference looks like this:
harbor.local/library/demo-nginx:1.21
Parts:
harbor.local: registry hostlibrary: Harbor projectdemo-nginx: repository name1.21: tag
Saved reports contain:
- project
- repository
- reference
- image
- generated timestamp
- severity summary
- full findings list
Example:
{
"project": "library",
"repository": "demo-nginx",
"reference": "1.21",
"image": "library/demo-nginx:1.21",
"generated_at": "2026-06-03T21:30:00+02:00",
"summary": {
"CRITICAL": 28,
"HIGH": 159,
"MEDIUM": 262,
"LOW": 209,
"UNKNOWN": 15
},
"findings": [
{
"id": "CVE-2024-56171",
"severity": "CRITICAL",
"package": "libxml2",
"version": "2.9.10",
"fixed_version": "2.9.10+patched",
"description": "Example vulnerability description"
}
]
}Full workflow:
export HARBOR_URL=http://harbor.local
export HARBOR_USER=admin
export HARBOR_PASSWORD=your-password
docker login harbor.local
./harbor-scanner publish-scan nginx:1.21 library demo-nginx 1.21 reports/demo-nginx-1.21.jsonExisting Harbor image only:
export HARBOR_URL=http://harbor.local
export HARBOR_USER=admin
export HARBOR_PASSWORD=your-password
./harbor-scanner scan-wait library demo-nginx 1.21 reports/demo-nginx-1.21.jsonFor shared or production usage, a Harbor robot account is usually better than a personal admin account.