How Harbor Performs Vulnerability Scanning: Architecture and Implementation

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, 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. 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 (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, 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, 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 (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 (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 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


# 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)

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.

Enable Automatic Scanning on Push (Helm Values)


# 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

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 – Startup initialization ensuring scanner registrations exist and setting Trivy as the default
  • src/pkg/scan/job.go – Asynchronous job implementation handling request submission, authentication, polling, and result retrieval
  • src/pkg/scan/vulnerability/vul.go – Vulnerability-specific handler for placeholder creation, report conversion, and summary generation
  • 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 – Data access layer for the scanner_registration table managing scanner lifecycle
  • 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 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →