Skip to content

denimoll/CVE-PaaS

Repository files navigation

CVE-Prioritizer-as-a-Service

Main info

API service for prioritize vulnerabilities. It's combination of:

Thresholds

  • Critical (if exists POC or Nuclei template or vulnerability in KEV)
  • High (CVSS > 6.0, EPSS > 0.2)
  • Medium (CVSS > 6.0, EPSS <= 0.2)
  • Low (CVSS <= 6.0, EPSS > 0.2)
  • Info (everything else)
  • Undefined (some trouble with CVSS and EPSS score for vulnerability)

Start

From ghcr.io (recommended)

docker run -d -p 8000:8000 --env PDCP_API_KEY=<your_key> --name cve-paas ghcr.io/denimoll/cve-paas:latest

From source

You need to install docker itself and then do next commands:

# git clone https://github.com/denimoll/CVE-PaaS.git
# cd CVE-PaaS
# bash start.sh

For the first start you must write API key from ProjectDiscovery (you need to be registered).

Configuration

Environment variables (pass via docker run --env):

Required

Variable Description
PDCP_API_KEY API key from ProjectDiscovery Cloud Platform — required by vulnx to fetch CVE data

Optional

Variable Default Description
CVSS_THRESHOLD 6.0 CVSS score boundary for High/Medium/Low
EPSS_THRESHOLD 0.2 EPSS score boundary for High/Low
ALLOW_ORIGINS * Comma-separated allowed CORS origins
CACHE_TTL_HOURS 0 Hours before cached result expires and re-fetches (0 = never expire)
CVE_PAAS_API_KEY (not set) If set, all /v1/ requests must include X-API-Key: <value> header — returns 401 otherwise
RATE_LIMIT 0 Max requests per minute per client IP across all endpoints (0 = disabled) — returns 429 when exceeded
DB_PATH cve_cache.db Path to the SQLite cache file inside the container
VULNX_BATCH_SIZE 5 CVEs per single vulnx API call — reduce if hitting PDCP rate limits
VULNX_BATCH_DELAY 2.0 Seconds to wait between batch calls to avoid PDCP rate limits (0 = no delay)
VULNX_RETRY_COUNT 3 Total fetch attempts per batch (1 = no retry) — retries on timeout or rate-limit errors
VULNX_RETRY_DELAY 5.0 Seconds to wait between retry attempts
ERROR_RECORD_TTL_HOURS 24 Hours before a failed-fetch error record is pruned from cache (0 = keep forever)
TRUSTED_PROXY_COUNT 0 Number of trusted reverse-proxy hops — enables reading the real client IP from X-Forwarded-For for rate limiting (e.g. 1 for nginx/Traefik in front of the service)
LOG_FORMAT text Log output format: text (human-readable) or json (one JSON object per line, for log aggregators like Loki/CloudWatch)

Note: RATE_LIMIT uses an in-memory counter and is effective only when running a single uvicorn worker (the default). With --workers N each process has its own counter and the effective limit is N × RATE_LIMIT.

Data persistence

The SQLite cache is stored at the path defined by DB_PATH (default /app/cve_cache.db). Without a volume mount the cache is lost on container restart. To persist it:

docker run -d -p 8000:8000 \
  --env PDCP_API_KEY=<your_key> \
  -v /path/on/host/cve_cache.db:/app/cve_cache.db \
  --name cve-paas ghcr.io/denimoll/cve-paas:latest

Usage

All API Endpoints described on localhost:8000/docs:

  • GET / - homepage / health check
  • GET /v1/get_info/{cve_id} - Main function: return info from vulnx (old cvemap) and determine priority
  • POST /v1/cve - Bulk lookup: accepts {"cve_ids": ["CVE-YYYY-NNNNN", ...]} (1–50 IDs), returns map of CVE ID → priority result
  • GET /v1/reget_info/{cve_id} - Force refresh: invalidate cache and re-fetch from vulnx
  • GET /v1/all_results - Return cached results with full raw info (without priorities); supports ?offset=0&limit=100 (max 1000)
  • POST /v1/retry_errors - Re-fetch all CVEs that previously failed to load (stored as error records)
  • DELETE /v1/results - Remove all cached results (use when results outdated)

Use Cases

1. Check a single CVE

The most common scenario — paste a CVE ID from a scanner report and get an instant priority verdict.

curl http://localhost:8000/v1/get_info/CVE-2021-44228
{
  "Priority": "Critical",
  "Details": {
    "CVSS": 10.0,
    "EPSS": 0.97527,
    "is_template": true,
    "is_exploited": true,
    "is_poc": true,
    "Links": {
      "POC": "https://github.com/...",
      "Nuclei templates": "https://github.com/projectdiscovery/nuclei-templates/...",
      "KEV": "https://www.cisa.gov/..."
    }
  }
}

Priority is Critical if any of: POC exists, Nuclei template exists, vulnerability is in CISA KEV. Otherwise scored by CVSS + EPSS thresholds.


2. Batch check from a scanner report

Got a list of CVEs from Trivy, Nessus, or any other scanner? Send them all at once — up to 50 per request, cache-aware.

curl -X POST http://localhost:8000/v1/cve \
  -H "Content-Type: application/json" \
  -d '{
    "cve_ids": [
      "CVE-2021-44228",
      "CVE-2023-44487",
      "CVE-2024-1337"
    ]
  }'
{
  "CVE-2021-44228": { "Priority": "Critical", "Details": { ... } },
  "CVE-2023-44487": { "Priority": "High",     "Details": { ... } },
  "CVE-2024-1337":  { "Priority": "Medium",   "Details": { ... } }
}

Only CVEs not already cached will trigger a vulnx call — the rest are served instantly from SQLite.

Note on transient fetch failures: When the PDCP upstream is temporarily unavailable or rate-limits your key, individual CVEs in a batch may fail even after retries. Those CVEs are stored in the cache as error records and returned with an "error" key instead of "Priority". They are automatically excluded from /v1/all_results and will be re-fetched on the next request that includes them. You can also recover them all at once with POST /v1/retry_errors (see use case 7).


3. Force refresh stale data

When a CVE status changes (e.g., a POC is published after initial lookup), refresh it without clearing the whole cache.

curl http://localhost:8000/v1/reget_info/CVE-2024-1337

This deletes the cached entry and re-fetches from vulnx in one step.


4. Review everything in cache

Browse all accumulated results — useful for audits or piping into another tool.

# First page
curl "http://localhost:8000/v1/all_results?limit=50&offset=0"

# Next page
curl "http://localhost:8000/v1/all_results?limit=50&offset=50"
{
  "total": 142,
  "offset": 0,
  "limit": 50,
  "results": {
    "CVE-2021-44228": { ... },
    "CVE-2023-44487": { ... }
  }
}

5. Protected deployment (team / self-hosted)

Lock the service with an API key so only authorized clients can use your vulnx quota.

# Start with auth enabled
docker run -d -p 8000:8000 \
  --env PDCP_API_KEY=<your_pdcp_key> \
  --env CVE_PAAS_API_KEY=mysecrettoken \
  --env RATE_LIMIT=120 \
  --name cve-paas ghcr.io/denimoll/cve-paas:latest

# Query with the key
curl -H "X-API-Key: mysecrettoken" \
  http://localhost:8000/v1/get_info/CVE-2021-44228

Requests without a valid X-API-Key header return 401. Rate limiting (here: 120 req/min per IP) returns 429 when exceeded.


6. Invalidate cache

Force all clients to re-fetch fresh data on next request — useful after a long gap or known stale period.

curl -X DELETE http://localhost:8000/v1/results
{ "Status": "OK", "Endpoint": "remove_results" }

7. Recover after upstream failure

After a PDCP outage or rate-limit window, some CVEs in a bulk request may be stored as error records. Recover them all in one call:

curl -X POST http://localhost:8000/v1/retry_errors
{
  "retried": 3,
  "results": {
    "CVE-2024-1111": { "Priority": "High", "Details": { ... } },
    "CVE-2024-2222": { "Priority": "Medium", "Details": { ... } },
    "CVE-2024-3333": { "error": "vulnx error: Rate limit exceeded" }
  }
}

retried is the number of error records found. CVEs that still fail are stored as error records again and can be retried later. You can tune retry behaviour with VULNX_RETRY_COUNT (default 3 total attempts) and VULNX_RETRY_DELAY (default 5 s between attempts).

Roadmap

Done

  • New flow: Publish to ghcr.io, update start instructions
  • Endpoints: Rework endpoint list (DELETE /results, proper HTTP codes)
  • Annotation: Add annotation to params for all endpoints
  • Settings: Configurable CVSS/EPSS thresholds via env
  • Optimization: SQLite cache (aiosqlite), FastAPI lifespan init
  • Security: Configurable ALLOW_ORIGINS, non-root Docker user, pinned vulnx version
  • Tests: Pytest suite with CI workflow

Planned

  • Cache TTL: Auto-expire cached results after configurable period (CACHE_TTL_HOURS)
  • Bulk endpoint: POST /v1/cve with array of CVE IDs for batch processing (1–50 IDs, cache-aware)
  • API auth: Optional X-API-Key header (CVE_PAAS_API_KEY env) to protect quota
  • Rate limiting: Per-client request throttling (RATE_LIMIT requests/minute, sliding window)
  • API versioning: /v1/ prefix for stable contract with consumers
  • Pydantic response models: Typed response schemas for OpenAPI codegen
  • Retry mechanism: Automatic retries with error record persistence and POST /v1/retry_errors
  • Reverse proxy support: TRUSTED_PROXY_COUNT for correct client IP behind nginx/Traefik
  • Structured logging: LOG_FORMAT=json for Loki/CloudWatch log aggregators
  • Prometheus metrics: GET /metrics endpoint (request count, latency, cache hit rate) for Prometheus + Grafana stack

About

CVE-Prioritizer-as-a-Service is an FastAPI server that returns additional information (CVSS, EPSS, KEV, POCs, Nuclei Template) about the vulnerability and determines the priority of criticality

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages