Skip to content

Repository files navigation

VulTrack

A vulnerability management system for tracking and triaging security vulnerabilities across your Ubuntu Linux infrastructure using agent-based scanning, OVAL definitions, and Ubuntu VEX data.

VulTrack was developed entirely with AI. There might be dragons. 🐉

The VulTrack Agent can be found in this repository.

Features

  • Dashboard: Overview of your vulnerability landscape with key metrics and charts
  • Server Management: View all monitored servers and their vulnerability status
  • Findings Browser: Search and filter vulnerability findings across all servers
  • Triage Queue: Efficiently assess and categorize high-severity vulnerabilities
  • Statistics: Trend analysis and detailed reports
  • Agent Management: Manage agents and enrollment keys
  • OVAL Integration: Automatic vulnerability detection using OVAL definitions (Ubuntu and others)
  • NVD Enrichment: CVE details enriched with CVSS scores, CWE IDs, and descriptions
  • ExploitDB Integration: Identify vulnerabilities with known exploits
  • Ubuntu VEX Integration: Automatic enrichment of findings with Canonical's VEX (Vulnerability Exploitability eXchange) data — surfaces Not Affected, Will Not Fix, and Under Investigation assessments from Ubuntu Security directly in your findings and triage queue
  • Scheduled Reports: Automated PDF reports delivered by email (weekly / monthly)
  • Server Groups: Organize servers into groups for targeted reporting and filtering
  • Jira Integration: Automatically create Jira tickets for relevant findings
  • AI Assessment: Optional advisory LLM assessment of CVEs — explains the attack vector and prerequisites and recommends a triage status, using a configurable model and an admin-defined infrastructure-context prompt. Advisory only; it never changes the human assessment. Requires an Anthropic API key
  • OIDC Authentication: Optional single sign-on via any OpenID Connect identity provider

Architecture

                                                         ┌────────────────┐
                                                         │  PostgreSQL    │
                                                         │  Port: 5432    │
                                                         └───────▲────────┘
                                                                 │
                                        ┌─────────────────┐      │
                              ┌───────▶│    Frontend     │      │
                              │         │    (React)      │      │
                              │         │    Port: 8080   │      │
┌───────────┐       ┌─────────┴─────┐   └─────────────────┘      │
│           │       │               │                            │
│  Browser  │─────▶│    Reverse    │   ┌─────────────────┐      │
│           │       │    Proxy      │─▶│    Backend      │──────┘
│           │       │               │   │    (Go/Fiber)   │
└───────────┘       │  Port: 443    │   │    Port: 8080   │
                    │               │   └─────────────────┘
┌───────────┐       │   /*  → FE    │           ▲
│           │       │  /api/* → BE  │           │
│  Agents   │─────▶│ /metrics → BE │───────────┘
│           │       │               │
└───────────┘       └───────────────┘

Components:

  • Frontend: React SPA served by Nginx (static files only)
  • Backend: Go/Fiber REST API
  • PostgreSQL: Database for all application data
  • Reverse Proxy: Routes requests to Frontend or Backend (not included, bring your own)
  • Agents: Lightweight agents on monitored servers that report package information

Quick Start

Important

If you don't enable OIDC (see below) VulTrack can be used without any authentication.

Prerequisites

  • Docker and Docker Compose
  • A reverse proxy (e.g., Caddy, Traefik, Nginx)
  • PostgreSQL database

Building the Images

# Build backend image
docker build -t vultrack-backend ./backend

# Build frontend image
docker build -t vultrack-frontend ./frontend

Example Docker Compose Setup

Below is an example using Caddy as a reverse proxy. Adjust according to your infrastructure.

docker-compose.yml:

services:
  caddy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
    depends_on:
      - frontend
      - backend
    restart: unless-stopped

  frontend:
    image: vultrack-frontend:latest
    restart: unless-stopped

  backend:
    image: vultrack-backend:latest
    environment:
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - POSTGRES_USER=vultrack
      - POSTGRES_PASSWORD=changeme
      - POSTGRES_DB=vultrack
      - LOG_LEVEL=info
    depends_on:
      - postgres
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=vultrack
      - POSTGRES_PASSWORD=changeme
      - POSTGRES_DB=vultrack
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  caddy_data:
  postgres_data:

Caddyfile:

vultrack.example.com {
    # Frontend (static files)
    handle {
        reverse_proxy frontend:8080
    }

    # Backend API
    handle /api/* {
        reverse_proxy backend:8080
    }

    # Backend metrics (Prometheus scrape endpoint)
    handle /metrics {
        reverse_proxy backend:8080
    }
}

Access the Application

  1. Start the services: docker compose up -d
  2. Access the web interface at https://vultrack.example.com
  3. Create an enrollment key in Admin → Enrollment Keys
  4. Deploy agents on your servers using the enrollment key

Configuration

All configuration is done via environment variables.

Backend Environment Variables

Variable Description Default
POSTGRES_HOST PostgreSQL hostname localhost
POSTGRES_PORT PostgreSQL port 5432
POSTGRES_USER PostgreSQL username vultrack
POSTGRES_PASSWORD PostgreSQL password vultrack
POSTGRES_DB PostgreSQL database name vultrack
BACKEND_PORT Port the backend listens on 8080
LOG_LEVEL Log level (debug, info, warn, error) info
TRIAGE_CVSS_THRESHOLD Minimum CVSS score for triage queue 7.0
CORS_ORIGINS Comma-separated origins for CORS (required when using OIDC) (empty)

Ubuntu VEX (optional)

VulTrack automatically downloads and syncs Canonical's VEX data (vex-all.tar.xz) to enrich findings with Ubuntu's own exploitability assessments. The sync runs on a configurable schedule.

Variable Description Default
VEX_SYNC_INTERVAL_HOURS How often the VEX data is refreshed (hours) 24

Scan Queue

Variable Description Default
SCAN_WORKERS Number of concurrent scan workers 5
SCAN_TIMEOUT Timeout per scan in seconds 600
SCAN_MAX_RETRIES Maximum retry attempts for failed scans 3
SCAN_QUEUE_SIZE Maximum scan queue depth 500

Email Notifications (optional)

When SMTP_ENABLED=true, VulTrack sends email reports via SMTP.

Variable Description Default
SMTP_ENABLED Enable email sending false
SMTP_HOST SMTP server hostname (empty)
SMTP_PORT SMTP server port 587
SMTP_USER SMTP username (leave empty for unauthenticated relay) (empty)
SMTP_PASSWORD SMTP password (empty)
SMTP_FROM Sender address (e.g. VulTrack <vultrack@example.com>) (empty)
SMTP_TLS TLS mode: starttls (port 587), tls (port 465), none (port 25) starttls
SMTP_HELO_HOST EHLO hostname sent to the relay. Many relays reject localhost; set to your server's FQDN if needed (OS hostname)

Jira Integration (optional)

When JIRA_ENABLED=true, a Jira ticket is automatically created whenever a finding is assessed as Relevant.

Variable Description Default
JIRA_ENABLED Enable Jira integration false
JIRA_BASE_URL Jira instance URL (e.g. https://your-org.atlassian.net) (empty)
JIRA_USER_EMAIL Jira user email for API authentication (empty)
JIRA_API_TOKEN Jira API token (empty)
JIRA_PROJECT_KEY Jira project key (e.g. SEC) (empty)
JIRA_ISSUE_TYPE Issue type for created tickets Task

AI Assessment (optional)

When ANTHROPIC_API_KEY is set, VulTrack can produce an advisory LLM assessment for a CVE: a plain-language explanation of the attack vector and prerequisites plus a recommended triage status and confidence. The result is advisory only — it never changes the human assessment.

The API key is environment-only and is never exposed through the API or UI. If the key is identity-linked (bound to a user identity rather than to a single workspace), also set ANTHROPIC_WORKSPACE_ID — such keys are not tied to one workspace, so the API rejects requests that do not name one (400 … anthropic-workspace-id is required). A workspace-scoped key needs no such setting. The rest of the configuration lives in Admin → AI Assessment: a master switch (Enable AI assessment), optional Auto-assess triage findings (assess every CVE entering the triage queue once), the model, and an infrastructure-context system prompt. Both the API key and the master switch must be enabled for assessments to run. CVEs can also be assessed on demand from the finding/triage views. A re-assessment of the same CVE is rate-limited to once every 30 minutes.

Variable Description Default
ANTHROPIC_API_KEY Anthropic API key. Enables the feature when set (empty)
ANTHROPIC_WORKSPACE_ID Workspace to act in (wrkspc_…). Required for identity-linked API keys (empty)
AI_ASSESSMENT_WORKERS Number of concurrent assessment workers 2
AI_ASSESSMENT_MAX_RETRIES Maximum retry attempts on transient errors 2
AI_ASSESSMENT_TIMEOUT Timeout per model request in seconds 60

Frontend Environment Variables

Variable Description Default
VITE_API_URL Backend API base URL used during the frontend build http://localhost:8080/api/v1

OIDC Authentication (optional)

When OIDC_ENABLED=true, the web UI requires users to sign in via an OpenID Connect identity provider (e.g. Keycloak, Auth0, Okta). When disabled, no login is required.

Variable Description Default
OIDC_ENABLED Enable OIDC login for the web UI false
OIDC_ISSUER IdP issuer URL (e.g. https://idp.example.com/realms/vultrack) (empty)
OIDC_CLIENT_ID OAuth2 client ID (empty)
OIDC_CLIENT_SECRET OAuth2 client secret (empty)
OIDC_REDIRECT_URL Backend callback URL (e.g. https://vultrack.example.com/api/v1/auth/callback) (empty)
OIDC_FRONTEND_URL Frontend URL for redirect after login (e.g. https://vultrack.example.com) /
OIDC_SCOPES OAuth2 scopes (space or comma separated) openid profile email

Setup:

  1. Create an OAuth2/OIDC client in your IdP with redirect URI = OIDC_REDIRECT_URL.
  2. Set CORS_ORIGINS to your frontend origin(s) so session cookies work (e.g. http://localhost:3000 or https://vultrack.example.com).
  3. The first user to sign in when no admin exists becomes an admin. Further admins can be assigned in Admin → Users.

The agent API (/api/v1/agent/*) is unchanged and uses enrollment keys and agent tokens, not OIDC.

Development

Backend (Go)

cd backend
go mod download
go run ./cmd/vultrack

Frontend (React)

cd frontend
npm install
npm run dev

Database

For development, you can start just the database:

docker compose up -d postgres

API Endpoints

Authentication

  • GET /api/v1/auth/login - Redirect to OIDC identity provider
  • GET /api/v1/auth/callback - OIDC callback (handles token exchange and session creation)
  • POST /api/v1/auth/logout - Invalidate current session
  • GET /api/v1/auth/me - Current user info (returns authEnabled: false when OIDC is disabled)

Dashboard

  • GET /api/v1/dashboard - Dashboard statistics

Servers

  • GET /api/v1/servers - List all servers
  • GET /api/v1/servers/:id - Get server details
  • GET /api/v1/servers/:id/findings - Get server findings
  • GET /api/v1/servers/:id/packages - Get server packages
  • GET /api/v1/servers/:id/groups - Get groups for a server
  • PUT /api/v1/servers/:id/groups - Set groups for a server
  • POST /api/v1/servers/:id/scan - Trigger a vulnerability scan
  • DELETE /api/v1/admin/servers/:id - Delete server

Findings

  • GET /api/v1/findings - List findings with filters (supports vexStatus query param: not_affected, will_not_fix, under_investigation)
  • GET /api/v1/findings/triage - Get triage queue (supports hideVexNotAffected=false to include not-affected findings; default is true)
  • GET /api/v1/findings/:id - Get finding details

CVEs

  • GET /api/v1/cves/:id - Get CVE details
  • GET /api/v1/cves/:id/servers - Get servers affected by CVE

Assessments

  • GET /api/v1/assessments - List all assessments
  • POST /api/v1/assessments - Create assessment
  • PUT /api/v1/assessments/:cveId - Update assessment
  • DELETE /api/v1/assessments/:cveId - Delete assessment

AI Assessments

  • GET /api/v1/ai-assessments - List AI assessments (filters: status, search; paginated)
  • GET /api/v1/ai-assessments/:cveId - Get the AI assessment for a CVE (404 if none yet)
  • POST /api/v1/findings/:cveId/ai-assess - Request an AI assessment (?force=true to re-assess; only for CVEs with active findings)

Reason Templates

  • GET /api/v1/reason-templates - List reason templates
  • POST /api/v1/reason-templates - Create reason template
  • PUT /api/v1/reason-templates/:id - Update reason template
  • DELETE /api/v1/reason-templates/:id - Delete reason template

Statistics

  • GET /api/v1/stats/severity - Severity breakdown
  • GET /api/v1/stats/trend - Finding trends over time
  • GET /api/v1/stats/top-servers - Most affected servers
  • GET /api/v1/stats/top-cves - Most widespread CVEs
  • GET /api/v1/stats/assessments-by-severity - Assessments grouped by severity

Reports

  • POST /api/v1/reports/generate - Generate a PDF report on demand
  • GET /api/v1/report-schedules - List report schedules
  • POST /api/v1/report-schedules - Create report schedule
  • GET /api/v1/report-schedules/:id - Get report schedule
  • PUT /api/v1/report-schedules/:id - Update report schedule
  • DELETE /api/v1/report-schedules/:id - Delete report schedule
  • POST /api/v1/report-schedules/:id/toggle - Enable or disable a schedule
  • POST /api/v1/report-schedules/:id/run-now - Run a schedule immediately

Scan Queue

  • GET /api/v1/scans - List scan jobs
  • GET /api/v1/scans/stats - Scan queue statistics
  • POST /api/v1/scans/:id/cancel - Cancel a queued scan
  • POST /api/v1/scans/:id/retry - Retry a failed scan

Agent API

  • POST /api/v1/agent/enroll - Register a new agent (requires X-Enrollment-Key header)
  • POST /api/v1/agent/report - Submit agent report (requires X-Agent-Token header)

Admin: Settings

  • GET /api/v1/admin/settings - Get application settings
  • PUT /api/v1/admin/settings - Update application settings

Admin: Users

  • GET /api/v1/admin/users - List users (requires OIDC)

Admin: Server Groups

  • GET /api/v1/admin/server-groups - List server groups
  • POST /api/v1/admin/server-groups - Create server group
  • GET /api/v1/admin/server-groups/:id - Get server group
  • PUT /api/v1/admin/server-groups/:id - Update server group
  • DELETE /api/v1/admin/server-groups/:id - Delete server group
  • GET /api/v1/admin/server-groups/:id/members - List group members
  • POST /api/v1/admin/server-groups/:id/members - Add member to group
  • DELETE /api/v1/admin/server-groups/:id/members/:serverId - Remove member from group

Admin: Enrollment Keys

  • GET /api/v1/admin/enrollment-keys - List enrollment keys
  • POST /api/v1/admin/enrollment-keys - Create enrollment key
  • PUT /api/v1/admin/enrollment-keys/:id - Update enrollment key
  • DELETE /api/v1/admin/enrollment-keys/:id - Delete enrollment key

Admin: API Tokens

  • GET /api/v1/admin/api-tokens - List API tokens (for the MCP interface)
  • POST /api/v1/admin/api-tokens - Create an API token (the full token is returned once)
  • DELETE /api/v1/admin/api-tokens/:id - Delete (revoke) an API token

Admin: Agents

  • GET /api/v1/admin/agents - List registered agents
  • PUT /api/v1/admin/agents/:id/approve - Approve pending agent
  • PUT /api/v1/admin/agents/:id/revoke - Revoke agent access
  • DELETE /api/v1/admin/agents/:id - Delete agent

Admin: OVAL Sources

  • GET /api/v1/admin/oval/distributions - List available distributions
  • GET /api/v1/admin/oval/sources - List enabled OVAL sources
  • POST /api/v1/admin/oval/sources - Enable OVAL source
  • PUT /api/v1/admin/oval/sources/:id/disable - Disable an OVAL source
  • DELETE /api/v1/admin/oval/sources/:id - Remove an OVAL source
  • POST /api/v1/admin/oval/sources/:id/sync - Trigger sync for a specific source
  • POST /api/v1/admin/oval/sync-all - Trigger sync for all enabled sources

Admin: NVD, ExploitDB & VEX

  • POST /api/v1/admin/nvd/sync - Trigger NVD CVE sync
  • GET /api/v1/admin/nvd/status - NVD sync status
  • POST /api/v1/admin/exploitdb/sync - Trigger ExploitDB sync
  • GET /api/v1/admin/exploitdb/status - ExploitDB sync status
  • GET /api/v1/admin/sync/status - Combined sync status
  • POST /api/v1/admin/vex/sync - Trigger Ubuntu VEX sync
  • GET /api/v1/admin/vex/status - VEX sync status and statement count

Admin: System

  • POST /api/v1/admin/system/reset - Reset all application data

Monitoring

  • GET /metrics - Prometheus metrics endpoint (no authentication required)

MCP Interface (AI Agents)

VulTrack exposes a Model Context Protocol server so that AI agents can query the vulnerability landscape and perform triage actions. It is built into the backend and served at:

POST /api/mcp

The transport is Streamable HTTP in stateless / JSON-response mode (plain request/response, no long-lived SSE stream).

Authentication

The MCP endpoint always requires an API token, regardless of whether OIDC is enabled — it is a machine interface and is never exposed unauthenticated. Tokens are created by admins under Admin → API Tokens in the UI:

  • Each token has a description (so it is clear what it is used for).
  • A token can optionally be marked read-only — it is then routed to a server that exposes only the query tools; the mutating tools are not visible to it at all.
  • A token can optionally be given an expiry date.

The full token (prefixed vt_) is shown only once at creation time; only its hash is stored. Present it as a bearer token:

Authorization: Bearer vt_<token>

Deleting a token revokes it immediately. All write actions are logged with the acting token's prefix and description for auditing.

Tools

Read tools (available to every token): list_servers, get_server, list_findings, get_finding, get_cve, list_cve_servers, get_ai_assessment, list_triage_queue, get_triage_config, list_assessments, get_dashboard_stats, get_severity_stats, get_trend_stats, get_top_servers, get_top_cves, get_assessments_by_severity, list_server_groups, get_server_group, get_server_group_members

Write tools (only available to non-read-only tokens): upsert_assessment, delete_assessment, trigger_server_scan, create_server_group, update_server_group, delete_server_group, set_server_group_members

All MCP tools share the same logic as the REST API / UI, including admin-configured settings. In particular, list_triage_queue honours the configured triage filter (vendor severities or CVSS threshold) and returns the same findings as the UI, and upsert_assessment creates a Jira ticket when the status is set to relevant and Jira is enabled — just like the UI.

The list tools (list_findings, list_triage_queue, list_assessments) are paginated: each call returns at most limit items (and the full total) along with a hasMore flag. To retrieve every entry, the agent repeats the call with offset increased by limit while hasMore is true.

To make results self-describing, the responses also report which filter produced them: list_triage_queue returns a filter object with the admin-configured settings (mode, vendor severities or CVSS threshold, include-unrated, hide-VEX-not-affected), and list_findings / list_assessments echo the effective filter (including defaults like includeResolved and sort order) in appliedFilter. The get_triage_config tool exposes the same triage settings directly.

Example client configuration

For an MCP client that supports Streamable HTTP servers (e.g. Claude Code / Claude Desktop):

{
  "mcpServers": {
    "vultrack": {
      "type": "http",
      "url": "https://vultrack.example.com/api/mcp",
      "headers": {
        "Authorization": "Bearer vt_your_token_here"
      }
    }
  }
}

Agent Deployment

Agents are lightweight scripts or binaries that run on monitored servers. They:

  1. Enroll with VulTrack using an enrollment key
  2. Periodically report system information and installed packages
  3. VulTrack matches packages against OVAL definitions to detect vulnerabilities

See docs/AGENT_API.md for the complete agent API specification.

For a deeper OIDC setup guide including IdP-specific instructions, see docs/OIDC_SETUP.md.

Ubuntu package vulnerability feed

Next to the OVAL files, Canonical publishes com.ubuntu.<codename>.pkg.json.xz — the dataset the pro cves command evaluates on a machine. VulTrack imports it server-side as a third source per Ubuntu release, alongside the USN and CVE OVAL feeds. Agents need no Ubuntu Pro client and no subscription; the feed is public.

It closes two gaps in the OVAL feeds:

  • Binary packages OVAL does not enumerate. OVAL lists the binaries of a source package in constant_variable elements, and that list is incomplete. On a 24.04 host with 10,269 packages installed, the feed found 3,329 findings the OVAL feeds miss (bluez-tests, libc6-prof, and similar).
  • CVSS scores, Canonical's triage notes and mitigations. The feed carries CVSS for 27,046 of the CVEs in the 24.04 data, plus free-text notes for 5,330 and mitigation advice for 105. VulTrack otherwise waits for the rate-limited NVD sync for scores and has no access to the notes at all. Scores and descriptions are backfilled into the CVE catalog for fields NVD has not delivered; wherever NVD has a value it keeps it.

It also records the archive pocket a fix comes from. An esm-infra/esm-apps pocket means the fix only reaches a machine with an Ubuntu Pro subscription, which is shown on the finding.

The feed also corrects OVAL's kernel findings. Ubuntu's OVAL identifies the running kernel by name pattern and cannot express an architecture, while the riscv64 kernel flavour is also called generic — so on an amd64 host the criteria for linux-riscv match too, and every CVE that affects only the riscv kernel is reported. That was 3,670 of 5,779 kernel findings on 24.04 with 6.8.0-124-generic. VulTrack now resolves which kernel source package the running kernel was built from and drops kernel findings the feed attributes to a different one. See docs/KERNEL_FLAVOUR_FILTER_PLAN.md.

Two deliberate limits:

  • The kernel stays with OVAL. 53 kernel source packages carry 95% of the feed's CVE statuses, and the feed sees installed kernel packages, so it would report every kernel still present in /boot. OVAL evaluates the kernel against the running kernel instead, which is more precise and far less noisy, so kernel source packages contribute no findings from this source — only the cross-check above.
  • A weaker source never overwrites a stronger one. A USN advisory is the most specific statement Ubuntu makes; the per-CVE OVAL adds the vendor's triage state (will_not_fix, deferred); the feed only distinguishes vulnerable from fixed. The pocket is the exception: only the feed knows it, so it is filled in regardless of which source owns the rest of the finding.

The feed is optional. A distribution without a feed URL never gets such a source, a release whose feed is missing fails only its own sync, and the source can be disabled individually under Admin → OVAL Sources. With it off, detection behaves exactly as it did with the OVAL feeds alone.

Upgrading

Database migrations run automatically on backend startup.

Already-enabled Ubuntu releases get a package feed source on the next start; the syncer imports it shortly afterwards. Expect the finding count to rise, mostly with affected entries for binary packages that were previously invisible.

One migration needs a note: OVAL tests now store the OVAL IDs of the object and state they reference, instead of the link being guessed from the test's own ID. Stored OVAL data cannot be converted, so the migration clears it and marks every enabled source for re-sync. The syncer re-downloads the feeds shortly after startup; until it finishes, scans find nothing. Re-scan your servers afterwards so their findings are rebuilt from the corrected data.

Roadmap

  • Support for more Linux distributions (Debian, RHEL, ...)
  • REST API for report export (CSV)
  • Multi-tenancy / team workspaces

License

MIT License

About

A vulnerability management system for tracking and triaging security vulnerabilities across your Ubuntu Linux infrastructure using agent-based scanning and OVAL definitions.

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages