Skip to content

Repository files navigation

kernelscan-dt-vex-sync

License

A project-aware VEX push connector for Dependency-Track. It pulls each KernelScan product's config-aware VEX and imports it into the matching DT project, so DT's findings carry KernelScan's not_affected / exploitable verdicts — the config-reachable ones actionable, the config-gated ones auto-marked Not Affected with the config-mapping justification.

Open-source client for the commercial KernelScan service. It runs beside your Dependency-Track instance and is the companion to kernelscan-dt-plugin — see SECURITY.md for its exact data flow.

Why it exists — the two channels

KernelScan's DT integration has two halves. They are separate on purpose: a DT v5 analyzer produces findings only — analysis decisions (not_affected + justification) come exclusively from VEX imports or CEL policies, never from an analyzer. So the plugin can surface the right findings but cannot triage them.

Channel Component Runs Delivers
Findings kernelscan-dt-plugin in-process in DT which kernel CVEs are config-reachable (incl. ones NVD is behind on)
Verdicts this connector beside DT, scheduled the config-aware not_affected / exploitable analysis, per project

Together: a project's kernel findings are fully triaged. You can run the verdict channel with either half — but paired with the plugin you get coverage and triage under one CVE-* / NVD identity.

How it works

Each cycle, for every DT project:

  1. Resolve the product. Read the project's CycloneDX BOM and find the Linux kernel component; resolve the KernelScan product exactly the way the plugin does (see below), so findings and verdicts always agree on the same product.
  2. Pull the VEX. GET /api/products/{id}/vex from KernelScan. The endpoint is ETag-aware, so an unchanged VEX is skipped (304) — no needless re-imports churning DT's audit trail.
  3. Apply it. PUT /api/v1/vex into the project. DT's importer binds each verdict to its finding by CVE id.

Per-project failures are logged and skipped; one bad project never aborts the cycle.

Install & run

API keys are read only from the environment, never from the command line — argv is world-readable (ps, docker inspect, shell history), so a security tool must not take secrets as flags:

pip install kernelscan-dt-vex-sync     # or: pip install .

export DT_API_KEY=odt_…                 # or DT_API_KEY_FILE=/run/secrets/dt
export KSCAN_API_KEY=ks_live_…          # or KSCAN_API_KEY_FILE=/run/secrets/kscan

# preflight: verify both endpoints + keys
dt-vex-sync --check --dt-url https://dtrack.example.com

# one cycle (CI / cron)
dt-vex-sync --once   # add --dry-run to resolve + count without pushing

# scheduled daemon (default every hour)
dt-vex-sync --interval 3600

Secrets — env / file only (no flag):

Env File variant Notes
DT_API_KEY DT_API_KEY_FILE DT team key (VIEW_PORTFOLIO + VULNERABILITY_ANALYSIS)
KSCAN_API_KEY KSCAN_API_KEY_FILE KernelScan account key ks_live_… (or KERNELSCAN_API_KEY)

The *_FILE variant reads the secret from a file path — the Docker/systemd/k8s secrets convention — so the value never appears in the environment table either.

Non-secret flags (each with an env var, handy for Docker):

Flag Env Default
--dt-url DT_URL
--kscan-url KSCAN_URL https://kernelscan.io
--state-file DT_SYNC_STATE_FILE /data/dt-vex-sync-state.json
--interval DT_SYNC_INTERVAL 3600

Other flags: --once, --dry-run, --force (ignore change-detection), --project <uuid> (one project), --check, --quiet.

Docker

A ready-to-edit docker-compose.yml runs it as a restart: unless-stopped service beside your DT, with the state file on a named volume.

DT API key permissions

Create a DT team key scoped to:

  • VIEW_PORTFOLIO — list projects and export their BOMs, and
  • VULNERABILITY_ANALYSIS — import the VEX.

No admin login is used.

Which product a project maps to

Resolution mirrors the analyzer plugin's selectProductId — deliberately, so the two channels never disagree:

  1. By explicit binding — a kernel component's kernelscan:product property pins a product by name or id. Required when two products share a kernel version but have different .config (e.g. a left/right appliance pair):

    {
      "type": "operating-system", "name": "linux", "version": "6.6.69",
      "cpe": "cpe:2.3:o:linux:linux_kernel:6.6.69:*:*:*:*:*:*:*",
      "properties": [{ "name": "kernelscan:product", "value": "Router-A [6.6.69]" }]
    }
  2. By kernel version — otherwise the version (from the component's purl or CPE, falling back to the DT project's version field) selects the product, if exactly one product has that version.

If several products share that version — a multi-arch account (same version imported for x86_64 + arm64), or a left/right appliance pair with different .config — the connector skips and logs a warning rather than guess. There is no architecture default: guessing an arch would risk applying the wrong .config's verdicts (and would diverge from the plugin, which dropped its arch config in v0.2.0). Pin the right product with a kernelscan:product binding.

The kernels you sync must exist as products in your KernelScan account — that's where the .config and its verdicts live.

Develop

pip install -e '.[test]'
pytest

Layout:

dt_vex_sync/
  cli.py           argparse + --once / polling loop
  runner.py        one sync cycle (pure orchestration) + change-detection
  resolver.py      project -> product (Python port of the plugin's selectProductId)
  dt_client.py     Dependency-Track REST (list projects, export BOM, PUT /vex)
  kscan_client.py  KernelScan REST (products, product VEX with ETag)
  state.py         per-project last-pushed ETag/hash (JSON, atomic writes)

resolver.py is a hand-maintained port of KernelScanVulnAnalyzer.{selectProductId,kernelVersionOf,productBindingOf} in kernelscan-dt-plugin. Keep the two in lock-step — if they diverge, a project could show one product's findings but be triaged with another's verdicts.

License

Apache License 2.0 — see LICENSE and NOTICE. Copyright © 2026 KernelScan UG (haftungsbeschränkt). The KernelScan service itself is commercial; this connector is the open-source client.

About

Project-aware VEX push connector: apply KernelScan config-aware verdicts to Dependency-Track projects (companion to kernelscan-dt-plugin)

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages