Skip to content

Runner

CVEScan runners make contact with a remote jobs API endpoint and authenticate themselves with a unique runner-token. A runner is launched in the foreground with a cvescan start-runner subcommand. It will initialize a jobs queue, a process pool with a maximum of n_workers and start a supervisor loop. Each worker in the pool will start a worker loop, consume jobs from the queue and report results back to the jobs API. Jobs are leased to the runner for a given period of time, which might need to be renewed (see lease renewal) during job's runtime. A job with an expired lease will not be able to report results to the jobs API.

Note

A runner can be started through the CVEScan docker image.

Configuration

endpoint and runner-token are configurable respectively from their corresponding CVESCAN_RUNNER_ENDPOINT, CVESCAN_RUNNER_TOKEN and CVESCAN_RUNNER_UPDATE_PERIOD environment variables or from the runner section in CVEScan's configuration file as:

runner:
  endpoint: ...
  token: ...
  update_period: ...
If both solutions are used, the configuration file will have primacy.

Note

Instead of setting the above environment variables, a runner can also be configured by mounting files containing the expected values in the container and setting CVESCAN_RUNNER_ENDPOINT_FILE, CVESCAN_RUNNER_TOKEN_FILE and CVESCAN_RUNNER_UPDATE_PERIOD_FILE environment variables to their corresponding paths.

Supervisor loop

The supervisor loop is responsible for putting jobs in the queue, renewing leases and enforcing the update period. It will keep running until interrupted by a signal. SIGINT is used for graceful shutdown, while SIGTERM is used for immediate shutdown. It will always start by putting a update job in the queue.

flowchart TD
    A[Start]
    A --> B[Init jobs queue, leases registry, worker pool]
    Z[End]

    subgraph Supervisor Loop
        B --> C[Put update job in queue, lease in registry]
        C --> selector

        selector{"Runner interrupted?"}
        freeworker{"Idle worker?"}

        selector -->|No| freeworker
        freeworker -->|Yes| D[Request jobs API]

        jobreceived{"Job pending?"}
        D --> jobreceived

        jobreceived -->|Yes| E[Put job in queue, lease in registry]

        leaseextensionrequired{"Any lease require extension?"}
        freeworker -->|No| leaseextensionrequired
        jobreceived -->|No| leaseextensionrequired
        E --> leaseextensionrequired

        leaseextensionrequired -->|Yes| F[Extend lease]

        updaterequired{"Update period elapsed?"}
        leaseextensionrequired -->|No| updaterequired
        F --> updaterequired

        updaterequired -->|Yes| G[Put update job in queue, lease in registry]

        delay["Polling delay"]
        updaterequired -->|No| delay
        G --> delay

        delay --> selector
    end

    selector -->|Yes| Z

Worker loop

The worker loop is responsible for consuming jobs from the queue, executing them and reporting results to the jobs API. It will keep running until the pool is terminated by the supervisor loop.

flowchart LR
  subgraph Worker Loop
    WA[Start]

    jobinqueue{"Get job from queue?"}

    WA --> jobinqueue
    jobinqueue -->|No| jobinqueue
    jobinqueue -->|Yes| WB[Execute job]
    WB --> WC[Report to jobs API]
    WC --> WD[Clear job lease from registry]
    WD --> jobinqueue
  end

Lease renewal & results reporting

A job lease is renewed by the supervisor loop when half of the allocated time has passed. If a job lease expires, the worker executing it will not be able to report results to the jobs API.

When a worker reports results to the jobs API, it will include the job's lease token in the request. Results reporting and lease renewal are subject to a race condition. Therefore each lease is protected by a lock which needs to be acquired for either operations.

Update job

An update job will trigger updates for the specified CVEScan datasources.

Scan job

The scan job will trigger a scan for the job encoded SBOM. By default it scans against the NVD datasource and the OSV connector. The scan job can be configured to specify which datasources are to be used for both detection and consolidation of vulnerabilities. If a linux kernel is present it will perform an advanced scan of the associated vulnerabilities.

flowchart LR
  subgraph Worker Loop
    SA[Start]
    SA --> SB[Scan components against enabled datasources]

    SB --> NVD[(NVD datastore)]
    SB --> OSV[(OSV connector)]

    SB --> SC[Consolidate vulnerabilities]

    SC --> NVD
    SC --> OSV
    SC --> UCT[(UCT repository)]
    SC --> EPSS[(EPSS datastore)]
    SC --> KEV[(KEV repository)]

    islinuxkernel{"Is linux kernel present?"}

    SC --> islinuxkernel
    islinuxkernel -->|No| SD
    islinuxkernel -->|Yes| SE[Linux kernel vulnerabilities filtering]

    SE --> LK[(Linux kernel repository)]
    SE --> SD

    SD[End]
  end