# How Harbor Performs Vulnerability Scanning: Architecture and Implementation

> Discover how Harbor performs vulnerability scanning. Learn about its pluggable architecture, asynchronous jobs, and integration with Trivy for robust security.

- Repository: [Harbor/harbor](https://github.com/goharbor/harbor)
- Tags: architecture
- Published: 2026-04-09

---

**Harbor decouples vulnerability scanning from its registry core through a pluggable adapter architecture that uses asynchronous jobs to orchestrate scans, process results via Trivy or third-party scanners, and store structured vulnerability data in Harbor's relational database.**

Harbor, the open-source container registry project under the CNCF (Cloud Native Computing Foundation), implements vulnerability scanning as a modular service that integrates multiple security scanners without binding them to the core registry logic. This extensible architecture allows operators to register different scanner implementations—from the default Trivy adapter to commercial solutions—while maintaining consistent APIs and data models for vulnerability reporting across the platform.

## Scanner Registration and Initialization

When Harbor starts, the system automatically ensures that a vulnerability scanner is registered and configured as the default. According to the source code in `goharbor/harbor`, this initialization logic resides in **[`src/pkg/scan/init.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/init.go)**, where the functions `EnsureScanners` and `EnsureDefaultScanner` verify that the Trivy adapter (or another configured scanner) exists in the database and is marked as the default.

Scanner registrations persist in the `scanner_registration` table, managed by the DAO layer in **[`src/pkg/scan/dao/scanner/registration.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/dao/scanner/registration.go)**. This module provides CRUD operations through functions like `AddRegistration`, `ListRegistrations`, and `SetDefaultRegistration`, allowing administrators to maintain multiple scanner backends while designating one as the system-wide default for new projects.

## The Scan Execution Flow

Harbor’s vulnerability scanning operates as an asynchronous workflow managed by the Job Service, which decouples scan execution from HTTP request paths to enable retries, concurrency limits, and graceful shutdowns. The process spans from request creation to final summary generation.

### Triggering Scans (Manual vs. Automatic)

A vulnerability scan can initiate through two primary pathways. First, automatic scanning occurs on image push when the `scan_on_push` policy is enabled for a project. Second, users can trigger manual scans via the Harbor API or UI. In both cases, the system calls `MakePlaceHolder` in **[`src/pkg/scan/vulnerability/vul.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/vulnerability/vul.go)** (lines 60-97) to create a placeholder report record in the database with a status of `Pending` before execution begins.

### Job Execution and Adapter Communication

The core execution logic lives in **[`src/pkg/scan/job.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/job.go)**, which implements the `Run` method for the Image Scan Job. When executed, the job:

1. Retrieves the scanner registration details from the database
2. Constructs a `v1.ScanRequest` object containing the artifact digest and repository information
3. Generates an **Authorization** token (supporting basic or bearer authentication) for the target scanner
4. Submits the scan request to the scanner’s **adapter** (such as the Trivy adapter) via its REST client

The scanner adapter—typically a lightweight HTTP service like the one defined in **`make/photon/trivy-adapter/`**—implements the Harbor Scanner API v1 specification. It receives the request, executes the actual vulnerability analysis (running the Trivy binary internally), and returns a job identifier for status tracking.

### Polling and Report Retrieval

After submission, the job enters a polling loop defined in `fetchScanReportFromScanner` (**[`src/pkg/scan/job.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/job.go)**, lines 25-34). It repeatedly queries the adapter via `client.GetScanReport` for each requested MIME type—commonly `application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0`—until the scanner reports completion or a timeout occurs. This design supports scanners that require significant time to download vulnerability databases or analyze large images.

## Report Processing and Storage

Once the raw report is retrieved, Harbor’s post-processing pipeline converts scanner-specific formats into Harbor’s internal relational schema. The `PostScan` function in **[`src/pkg/scan/vulnerability/vul.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/vulnerability/vul.go)** (lines 51-58) orchestrates this translation using `postprocessors.Converter`, which transforms Trivy’s native JSON output into structured vulnerability records compatible with Harbor’s database model.

The converted data persists through `reportManager.UpdateReportData`, updating the previously created placeholder record with actual vulnerability counts, severity levels, and package information. This storage layer handles multiple report formats simultaneously, as modern scanners can provide Software Bill of Materials (SBOM) data alongside traditional vulnerability reports through MIME-type-driven content negotiation.

## Accessing Vulnerability Data

When users view vulnerability information through the Harbor UI or API, the system aggregates stored reports through the `GetSummary` method in **[`src/pkg/scan/vulnerability/vul.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/vulnerability/vul.go)** (lines 73-84). This function collects all available reports for an artifact—including SBOM data if present—and generates the final summary view showing severity distributions, fix versions, and scan timestamps. The handler layer in **[`src/pkg/scan/handler.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/handler.go)** routes these requests to the appropriate scan handler implementation based on the report type requested.

## Practical Configuration Examples

### Trigger a Manual Scan via REST API

```bash

# Scan an artifact (digest) in project "library", repository "nginx"

curl -u admin:Harbor12345 -X POST \
  "https://harbor.example.com/api/v2.0/projects/library/repositories/nginx/artifacts/sha256:abcd1234/scan"

```

Harbor responds with `202 Accepted`, creates a scan job, and updates the placeholder status to `Running`.

### Register a Third-Party Scanner (Aqua Example)

```bash
curl -u admin:Harbor12345 -X POST \
  "https://harbor.example.com/api/v2.0/system/scanners" \
  -H "Content-Type: application/json" \
  -d '{
        "name":"aqua",
        "url":"http://aqua-scanner:8080",
        "vendor":"Aqua",
        "disabled":false,
        "is_default": false,
        "skip_cert_verify": false,
        "auth":{
          "type":"basic",
          "secret":"<base64(username:password)>"
        }
      }'

```

This registration stores the adapter endpoint and credentials in the `scanner_registration` table via the DAO functions in [`registration.go`](https://github.com/goharbor/harbor/blob/main/registration.go).

### Enable Automatic Scanning on Push (Helm Values)

```yaml

# values.yaml for Harbor Helm chart

trivy:
  enabled: true                # deploy the Trivy adapter

  
core:
  scanAllPolicy:
    type: "daily"              # schedule a daily full scan

    schedule: "0 2 * * *"      # 2 AM UTC

```

When `scan_all_policy` is configured, Harbor schedules periodic jobs that iterate all repositories and enqueue vulnerability scanning jobs automatically.

### Retrieve Vulnerability Summary

```bash
curl -u admin:Harbor12345 \
  "https://harbor.example.com/api/v2.0/projects/library/repositories/nginx/artifacts/sha256:abcd1234/additions/vulnerabilities"

```

This endpoint calls `GetSummary` to aggregate reports and returns high-level severity counts alongside detailed vulnerability listings.

## Core Implementation Files

Understanding Harbor’s vulnerability scanning requires familiarity with these key source files:

- **[`src/pkg/scan/init.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/init.go)** – Startup initialization ensuring scanner registrations exist and setting Trivy as the default
- **[`src/pkg/scan/job.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/job.go)** – Asynchronous job implementation handling request submission, authentication, polling, and result retrieval
- **[`src/pkg/scan/vulnerability/vul.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/vulnerability/vul.go)** – Vulnerability-specific handler for placeholder creation, report conversion, and summary generation
- **[`src/pkg/scan/handler.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/handler.go)** – Registry of scan handlers for different report types (vulnerability, SBOM) used by the job service
- **[`src/pkg/scan/dao/scanner/registration.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/dao/scanner/registration.go)** – Data access layer for the `scanner_registration` table managing scanner lifecycle
- **[`src/pkg/scan/postprocessors/converter.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/postprocessors/converter.go)** – Translation layer converting Trivy JSON to Harbor’s relational vulnerability schema
- **`make/photon/trivy-adapter/`** – Build artifacts and entry points for the Trivy adapter implementing the Harbor Scanner API

## Summary

- Harbor uses a **plugin architecture** that decouples the registry from specific scanning engines through the Scanner API v1 specification.
- The **Job Service** manages asynchronous scan execution, enabling retries and preventing HTTP request timeouts during long-running analyses.
- `EnsureScanners` in [`init.go`](https://github.com/goharbor/harbor/blob/main/init.go) automatically registers the default Trivy adapter on system startup.
- Scan reports follow a **placeholder-first pattern**, where database records are created immediately with `Pending` status and updated after the `postprocessors.Converter` transforms raw scanner output.
- The system supports **multiple MIME types**, allowing scanners to provide vulnerability data, SBOMs, or other formats through the same adapter interface.
- Authentication between Harbor and scanner adapters supports both **basic auth** and **bearer tokens**, configured per-registration in the database.

## Frequently Asked Questions

### What is the default vulnerability scanner in Harbor?

Harbor ships with **Trivy** (Aqua Security’s scanner) as the default vulnerability scanner. On startup, the `EnsureDefaultScanner` function in [`src/pkg/scan/init.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/init.go) automatically registers the Trivy adapter and sets it as the system default, though administrators can register additional scanners and change the default through the API or UI.

### How does Harbor authenticate with external vulnerability scanners?

Harbor generates **Authorization** tokens for each scan request based on the scanner registration configuration stored in the database. The `Run` method in [`src/pkg/scan/job.go`](https://github.com/goharbor/harbor/blob/main/src/pkg/scan/job.go) constructs these tokens to support both **basic authentication** (username:password base64-encoded) and **bearer tokens**, passing them to the scanner adapter’s REST API when submitting `v1.ScanRequest` objects.

### Can Harbor automatically scan images when they are pushed?

Yes, Harbor supports **scan-on-push** functionality. When enabled for a project, the system automatically creates scan jobs via the `MakePlaceHolder` function whenever new artifacts are pushed to the registry. Additionally, the `scanAllPolicy` configuration allows administrators to schedule periodic scans of all repositories using cron expressions in the Helm values or system settings.

### What happens if a vulnerability scan fails or times out?

The Job Service handles failures through its polling mechanism in `fetchScanReportFromScanner`. If a scanner does not return results within the configured timeout or returns an error status, Harbor updates the placeholder report’s status to `Error` and preserves the failure message. The asynchronous architecture ensures that registry operations continue uninterrupted while scan failures are logged and visible through the API and UI for troubleshooting.