Vuln Commander is an open-source Application Security platform. It aggregates findings from DevSecOps scanners and service connectors into a single database, exposes a REST API, provides a web UI for vulnerability management, and ships Grafana dashboards for analytics.
- Platform overview
- Web UI
- Grafana dashboards
- Technology stack
- Architecture and data model
- SDK
- Clean install and run
The platform provides centralized vulnerability tracking across repositories:
- GitHub service connector β registers projects (repositories), default branch metadata, and the latest commit.
- DevSecOps connectors β periodically clone projects, run scanners, and sync findings with the API:
- GSS (secrets) β Nosey Parker
- IAC β KICS
- SCA Vuln / SCA License β Trivy
- API β stores projects, vulnerabilities, and scan history; JWT authentication for users and connectors; role-based access control.
- Web UI β dark-themed browser interface (Lunaris design) for dashboards, project registry, connector monitoring, and vulnerability triage.
- Dashboards β Grafana reads aggregated PostgreSQL views (overall stats and per-scanner breakdowns).
| Area | Description |
|---|---|
| Projects | Repository registration, teams (ti / sandbox), status (active / archived) |
| Vulnerabilities | Bulk create/update/close; lifecycle: new β closed / reopened |
| Connectors | Accounts with scope service or devsecops; per-project scan history |
| Users | Roles user / admin; access + refresh JWT |
| Observability | Connector logs in Graylog (GELF UDP), metrics in Grafana |
The platform ships a server-rendered web interface at /api/v1/. Pages share a common shell: sidebar navigation, user profile with logout, and a dark Lunaris theme (JetBrains Mono + Inter, orange accent).
Authentication uses HTTP-only cookies (vc_access_token, vc_refresh_token). After sign-in, the session is renewed automatically while you browse. Web session lifetime: access token 15 minutes, refresh token 30 minutes. Connector API access via Bearer JWT is unchanged (5 / 30 minutes).
Open /api/v1/auth/ and sign in with a platform user email and password. After login you are redirected to the home page (or the page you originally requested).
URL: /api/v1/
Dashboard with live platform metrics (open vulnerabilities, active projects, connectors, scans today) and quick links to pgAdmin, Grafana, and Graylog.
URL: /api/v1/projects
Registry of all tracked repositories. The table supports search, pagination, and configurable page size. Each row shows team, status, last commit timestamp, open vulnerability count, and an Open action that opens vulnerability management for that project.
URL: /api/v1/connectors
Monitor scanner and service integrations: connector name, scope (service / devsecops), runtime status (Running, Waiting, Error, Inactive), and last run time. The table refreshes automatically every minute; the footer shows Last updated with the timestamp of the latest data fetch.
URL: /api/v1/vuln/manage/{project_name}
Per-project triage view. Filter by status and severity, sort by created/closed dates, select rows for bulk actions, and open individual findings. Pagination and page size match the shared data-table controls used across the UI.
URL: /api/v1/vuln/details/{vuln_id}
Single-finding view with severity, status, file location, code snippet, metadata (commit, rule, branch), and navigation between related vulnerabilities in the same project. Actions: mark as False Positive, create task (in development).
| Page | URL |
|---|---|
| Sign in | /api/v1/auth/ |
| Home | /api/v1/ |
| Projects | /api/v1/projects |
| Connectors | /api/v1/connectors |
| Vulnerabilities (project) | /api/v1/vuln/manage/{project_name} |
| Vulnerability detail | /api/v1/vuln/details/{vuln_id} |
Grafana provides analytics on top of aggregated PostgreSQL views. The vuln-commander dashboard is provisioned automatically from grafana/main_dashboard.json and reads data from the Vuln Commander Postgres datasource.
URL: http://localhost:3000 (credentials: GF_SECURITY_ADMIN_USER / GF_SECURITY_ADMIN_PASSWORD from .env)
You can also open Grafana from the Home page quick actions (/api/v1/ β Open Grafana).
The dashboard exposes template variables for slicing data across the whole report:
| Variable | Description |
|---|---|
project_name |
Filter by repository / project |
control_type |
Filter by scanner (gss, iac, sca_vuln, sca_license) |
severity |
Filter by finding severity |
team |
Filter by project team (ti, sandbox) |
Per-project summary table: vulnerability counts by severity (info β critical), last commit time, and links to the project repository and vulnerability management UI.
High-level breakdowns: vulnerabilities by severity, status, scanner type, and a combined scanner + severity bar chart. Use this section for portfolio-wide trends and triage prioritization.
Scanner-specific analytics:
- IAC β findings by cloud/platform (Terraform, Kubernetes, CloudFormation, etc.) and by CWE.
- SCA β top packages with the most vulnerability occurrences across scanned projects.
Drill-down tables for individual findings:
| Panel | Content |
|---|---|
| GSS vulnerabilities details | Secret findings: severity, status, filepath |
| IAC vulnerabilities details | Misconfiguration rules and CWE references |
| SCA license details | License identifiers with SPDX links |
| SCA vulnerabilities details | CVE titles and descriptions |
Panels query SQL views created during API migration (api/migrations/views_generation_spec):
vc_view_general_vulnerabilities_statisticβ project summaryvc_view_project_vulnerabilities_statisticβ per-project detailvc_view_gss_vulnerabilities_statistic,vc_view_iac_*,vc_view_sca_*β per-scanner breakdown
| Component | Technology |
|---|---|
| API | Python 3.12, FastAPI, Uvicorn, Gunicorn |
| ORM / migrations | SQLAlchemy 2, Alembic, asyncpg |
| Schema validation | Pydantic v2 |
| Database | PostgreSQL (Bitnami image) |
| Authentication | JWT (PyJWT), bcrypt |
| UI templates | Jinja2, Lunaris CSS (api/src/static/css/lunaris.css) |
| Component | Technology |
|---|---|
| HTTP client | aiohttp |
| Scheduler | cron-converter (Scheduler) |
| Git operations | GitPython |
| Logging | loguru, pygelf β Graylog |
| Dependency management | uv |
| Shared schemas | shared/ package (vuln-commander-shared) |
| Service | Purpose | Host port |
|---|---|---|
api |
REST API + static assets | 8000 |
db |
PostgreSQL | 5432 |
db-ui |
pgAdmin | 8080 |
github, gss, iac, sca-license, sca-vuln |
Connectors | β |
graylog + graylog-datanode + graylog-mongodb |
Centralized logs | 9000 (UI) |
grafana |
Dashboards | 3000 |
- Nosey Parker (GSS)
- KICS (IAC)
- Trivy (SCA)
flowchart LR
subgraph connectors [Connectors]
GH[GitHub service]
GSS[GSS]
IAC[IAC]
SCA_V[SCA Vuln]
SCA_L[SCA License]
end
SDK[Vuln Commander SDK]
API[FastAPI API]
PG[(PostgreSQL)]
GL[Graylog]
GF[Grafana]
GH --> SDK
GSS --> SDK
IAC --> SDK
SCA_V --> SDK
SCA_L --> SDK
SDK -->|JWT REST| API
API --> PG
connectors -->|GELF| GL
GF -->|SQL views| PG
vuln-commander/
βββ api/ # REST API, Alembic migrations, UI templates
βββ sdk/ # Client library for connectors
βββ shared/ # Shared Pydantic schemas and enums
βββ connectors/
β βββ connectors_spec.yml # Connector registry for DB bootstrap
β βββ service/github/ # Service connector
β βββ devsecops/ # GSS, IAC, SCA-*
βββ grafana/ # Provisioning and dashboards
βββ static/ # Docs assets, SQL for views
βββ docker-compose.yml
- GitHub reads a list of repository URLs on a schedule and calls
POST /api/v1/projectsβ projects and commits are stored in the database. - A DevSecOps connector calls
GET /api/v1/projects/{connector_type}, clones the repository, and runs a scanner. - The SDK performs vulnerability synchronization (
process_vulns): new findings are created, missing ones are closed, previously closed ones are reopened when detected again. - After a scan, history is recorded via
POST /api/v1/connectors/history(severity counters). - Grafana builds reports from SQL views (
vc_view_*).
| Prefix | Purpose | Called by |
|---|---|---|
/auth |
Issue and refresh JWT | Users, connectors |
/users |
User management | Admin |
/projects |
Project CRUD / scanner project list | GitHub (service), connectors (devsecops) |
/vuln |
Bulk vulnerability operations, HTML UI pages | DevSecOps connectors, browser |
/connectors |
Connector management, scan history, UI data API | Admin, DevSecOps, browser |
Access control is based on JWT scope: user (admin/user role), connector (scope service / devsecops).
| Table | Purpose |
|---|---|
vc_project |
Project: name, team, clone URL, branch, status |
vc_commit |
Latest known commit for a project |
vc_user |
Platform users |
vc_connector |
Connector accounts |
vc_refresh_token |
Refresh tokens (user or connector) |
vc_vuln |
Vulnerabilities (unique hash) |
vc_connector_scan_history |
Severity snapshot after a scan |
- UserTeam:
ti,sandbox - UserRole:
user,admin - ProjectStatus:
active,archived - ConnectorScope:
service,devsecops - ConnectorType:
iac,gss,sca_license,sca_vuln,github - VulnSeverity:
info,low,medium,high,critical - VulnStatus:
new,awaiting_review,closed,false_positive,reopened
Finding uniqueness is defined by an MD5 over:
project_id, connector_type, severity, filepath, line, custom_fields (JSON).
The same formula is used in shared/schemas/vuln.py and sdk.src.utils.helpers.generate_vuln_hash.
Created during migration from api/migrations/views_generation_spec:
vc_view_general_vulnerabilities_statisticβ project summaryvc_view_project_vulnerabilities_statisticβ per-project detailvc_view_gss_vulnerabilities_statistic,vc_view_iac_*,vc_view_sca_*β per-scanner breakdown
The file connectors/connectors_spec.yml defines connectors for one-time registration on API startup (see api/docker-entrypoint.sh):
- name: github
env: GITHUB # β CONNECTOR_GITHUB_UUID / CONNECTOR_GITHUB_PASSWORD
scope: service
type: githubPython library for building connectors: HTTP client to the API, vulnerability sync, cron scheduler, and Graylog logging.
Full class and method reference: sdk/README.md
Quick example:
from sdk import Scheduler, VulnCommanderSDK
from shared.schemas.constants import ConnectorType
async def main():
scheduler = Scheduler("0 12 */1 * *") # cron
for _ in scheduler:
async with VulnCommanderSDK(connector_id, connector_password) as sdk:
projects = await sdk.get_projects(ConnectorType.gss)
# ... scan ...
await sdk.process_vulns(actual_vulns, project.vulns)Inside the Docker network, the default API URL is http://api:8000/api/v1 (sdk/src/utils/config.py).
- Docker and Docker Compose v2
- ~8 GB free RAM (Graylog + PostgreSQL + connectors)
- Internet access to pull/build images
git clone <repository-url> vuln-commander
cd vuln-commander
cp .env.example .envEdit .env:
- PostgreSQL, pgAdmin, Grafana, and Graylog passwords (
GRAYLOG_PASSWORD_SECRETβ₯ 16 characters) GRAYLOG_ROOT_PASSWORD_SHA2β SHA-256 hex of the Graylog admin passwordADMIN_*β API admin account (created only when the database has no users yet)CONNECTOR_*_UUIDandCONNECTOR_*_PASSWORDβ must matchCONNECTOR_ID/CONNECTOR_PASSWORDin each connectorβs.env
Create a .env file for each service in docker-compose.yml (not committed to the repo):
connectors/service/github/.env
CONNECTOR_ID=<same UUID as CONNECTOR_GITHUB_UUID>
CONNECTOR_PASSWORD=<same password as CONNECTOR_GITHUB_PASSWORD>
CRON_SCHEDULE=0 12 */1 * *
PARALLEL_TASKS_COUNT=2
GRAYLOG_UDP_PORT=12201connectors/devsecops/gss/.env (same pattern for iac, sca-license, sca-vuln with the matching UUIDs from .env):
CONNECTOR_ID=<CONNECTOR_GSS_UUID>
CONNECTOR_PASSWORD=<CONNECTOR_GSS_PASSWORD>
CRON_SCHEDULE=0 12 */1 * *
PARALLEL_TASKS_COUNT=2
VULNS_REQUEST_BATCH_SIZE=100
GRAYLOG_UDP_PORT=12201mkdir -p connectors/service/github
cat > connectors/service/github/projects.txt <<'EOF'
https://github.com/org/example-repo.git
EOFOne clone URL per line. The file is listed in .gitignore.
docker compose up -d --buildOn first api startup:
- Waits for PostgreSQL to become ready
- Runs
alembic upgrade head - Creates the admin user (if the DB is empty) and connectors from
connectors_spec.yml
Full database initialization may take 1β2 minutes (PostgreSQL healthcheck start_period is up to 120 s).
| Service | URL | Notes |
|---|---|---|
| Web UI (Home) | http://localhost:8000/api/v1/ | Sign in required; metrics and quick links |
| Sign in | http://localhost:8000/api/v1/auth/ | Platform user email + password |
| Projects | http://localhost:8000/api/v1/projects | Project registry and vulnerability entry point |
| Connectors | http://localhost:8000/api/v1/connectors | Live connector status (auto-refresh every 60 s) |
| API / OpenAPI | http://localhost:8000/docs | Swagger UI |
| pgAdmin | http://localhost:8080 | Credentials from .env |
| Grafana | http://localhost:3000 | Pre-provisioned vuln-commander dashboard; GF_SECURITY_ADMIN_* |
| Graylog | http://localhost:9000 | Root password from SHA2 in .env |
| Vulnerability management | http://localhost:8000/api/v1/vuln/manage/{project_name} | Open from Projects table after data is available |
API authentication for connectors and scripts: POST /api/v1/auth/ with Basic Auth (CONNECTOR_ID + connector password, or user email + password for programmatic access).
# Stop
docker compose down
# Remove volumes (clean DB, Graylog, Grafana)
docker compose down -vAfter down -v, the next up will run migrations and bootstrap again.
# API (from repo root; PostgreSQL must be running)
export PYTHONPATH=$(pwd)
export POSTGRESQL_HOST=localhost
# ... remaining POSTGRESQL_* from .env
uv sync --project api
uv run --project api alembic -c api/alembic.ini upgrade head
uv run --project api python -m api.src.core.orm.initialisation.session
uv run --project api uvicorn api.src.main:app --reload --host 0.0.0.0 --port 8000Run connectors from the sdk/ directory with PYTHONPATH pointing at the monorepo root and API_BASE_URL=http://localhost:8000/api/v1 (adjust sdk/src/utils/config.py or move the URL into an environment variable if needed).
See LICENSE.









