A modern, secure, and lightweight web UI for browsing, filtering, and visualizing Trivy vulnerability reports.
- Upload Trivy
.json,.spdx.json,.cdx.json, or.tarvulnerability reports - Drag-and-drop file uploads with file type, size, and JSON structure validation
- Real-time toast notifications for success/failure feedback
- Sorting by Artifact Name, Timestamp, Critical, High, Medium, and Low vulnerabilities
- Pagination with rows-per-page selection and page state stored in URL
- Full text search by Artifact name
- View report details:
- Severity breakdown Pie Chart
- Filter vulnerabilities by Severity, Package Name, CVE ID
- Direct links to CVE databases
- Optimized for both Development and Production environments
- Dark Mode ready (TailwindCSS)
- Strict backend validation (schema, file size, safe filenames)
- Timestamps automatically adjusted to your configured timezone
- Lightweight footprint (~100MB images)
- Internal API metrics (
/api/metricsβ total number of reports)
Frontend
- React (Vite)
- TypeScript
- TailwindCSS
- React Router
- Recharts (Charts library)
- React Hot Toast (Notifications)
Backend
- FastAPI (Python 3.11+)
- Uvicorn (ASGI server)
- Pydantic + pydantic-settings
- SQLAlchemy Core (Optional for DB backends)
- dotenv (.env support)
- Async/Await optimized
DevOps
- Docker & Docker Compose
- Clean separation of frontend/backend
- Environment overrides for Dev/Prod
- Health checks and metrics endpoints
git clone https://github.com/your-org/trivy-ui.git
cd trivy-uiBackend .env (inside backend/.env):
# Backend environment
ENV=development
TIMEZONE=Asia/Jerusalem
UVICORN_HOST=0.0.0.0
UVICORN_PORT=8000
# Storage backend options: filesystem / sqlite / postgres
DB_BACKEND=filesystem
FILESYSTEM_STORAGE_DIR=backend/app/storage/reports
# POSTGRES_URL=postgresql+asyncpg://user:password@localhost:5432/trivyui
# SQLITE_PATH=backend/trivy_ui.dbdocker-compose up --build- Frontend: http://localhost:3000
- Backend: http://localhost:8000
.
βββ ./CONTRIBUTING.md
βββ ./LICENSE
βββ ./README.md
βββ ./backend
βΒ Β βββ ./backend/Dockerfile
βΒ Β βββ ./backend/README.md
βΒ Β βββ ./backend/app
βΒ Β βΒ Β βββ ./backend/app/api
βΒ Β βΒ Β βΒ Β βββ ./backend/app/api/routes.py
βΒ Β βΒ Β βββ ./backend/app/core
βΒ Β βΒ Β βΒ Β βββ ./backend/app/core/config.py
βΒ Β βΒ Β βΒ Β βββ ./backend/app/core/database.py
βΒ Β βΒ Β βΒ Β βββ ./backend/app/core/exception_handlers.py
βΒ Β βΒ Β βββ ./backend/app/main.py
βΒ Β βΒ Β βββ ./backend/app/models
βΒ Β βΒ Β βΒ Β βββ ./backend/app/models/report.py
βΒ Β βΒ Β βββ ./backend/app/schemas
βΒ Β βΒ Β βΒ Β βββ ./backend/app/schemas/report.py
βΒ Β βΒ Β βββ ./backend/app/storage
βΒ Β βΒ Β βββ ./backend/app/storage/base.py
βΒ Β βΒ Β βββ ./backend/app/storage/factory.py
βΒ Β βΒ Β βββ ./backend/app/storage/filesystem.py
βΒ Β βΒ Β βββ ./backend/app/storage/postgres.py
βΒ Β βΒ Β βββ ./backend/app/storage/reports
βΒ Β βΒ Β βββ ./backend/app/storage/sqlite.py
βΒ Β βββ ./backend/logging.yaml
βΒ Β βββ ./backend/logs
βΒ Β βΒ Β βββ ./backend/logs/README.md
βΒ Β βββ ./backend/requirements.txt
βββ ./docker-compose.override.yml
βββ ./docker-compose.prod.yml
βββ ./docker-compose.yml
βββ ./frontend
βΒ Β βββ ./frontend/Dockerfile
βΒ Β βββ ./frontend/Dockerfile.dev
βΒ Β βββ ./frontend/README.md
βΒ Β βββ ./frontend/dist
βΒ Β βΒ Β βββ ./frontend/dist/assets
βΒ Β βΒ Β βΒ Β βββ ./frontend/dist/assets/index-CpjW13Wg.css
βΒ Β βΒ Β βΒ Β βββ ./frontend/dist/assets/index-Gatrr9HR.js
βΒ Β βΒ Β βββ ./frontend/dist/index.html
βΒ Β βββ ./frontend/eslint.config.js
βΒ Β βββ ./frontend/index.html
βΒ Β βββ ./frontend/nginx
βΒ Β βΒ Β βββ ./frontend/nginx/default.conf
βΒ Β βΒ Β βββ ./frontend/nginx/nginx.conf
βΒ Β βββ ./frontend/postcss.config.js
βΒ Β βββ ./frontend/public
βΒ Β βββ ./frontend/src
βΒ Β βΒ Β βββ ./frontend/src/App.css
βΒ Β βΒ Β βββ ./frontend/src/App.tsx
βΒ Β βΒ Β βββ ./frontend/src/api.ts
βΒ Β βΒ Β βββ ./frontend/src/components
βΒ Β βΒ Β βΒ Β βββ ./frontend/src/components/Chart.css
βΒ Β βΒ Β βΒ Β βββ ./frontend/src/components/LoadingSpinner.tsx
βΒ Β βΒ Β βΒ Β βββ ./frontend/src/components/ReportDetail.tsx
βΒ Β βΒ Β βΒ Β βββ ./frontend/src/components/ReportsList.tsx
βΒ Β βΒ Β βΒ Β βββ ./frontend/src/components/UploadForm.tsx
βΒ Β βΒ Β βββ ./frontend/src/index.css
βΒ Β βΒ Β βββ ./frontend/src/main.tsx
βΒ Β βΒ Β βββ ./frontend/src/vite-env.d.ts
βΒ Β βββ ./frontend/tailwind.config.js
βΒ Β βββ ./frontend/vite.config.ts
βββ ./k8s
βΒ Β βββ ./k8s/backend
βΒ Β βΒ Β βββ ./k8s/backend/backend-deployment.yaml
βΒ Β βΒ Β βββ ./k8s/backend/backend-pv.yaml
βΒ Β βΒ Β βββ ./k8s/backend/backend-pvc.yaml
βΒ Β βΒ Β βββ ./k8s/backend/backend-service.yaml
βΒ Β βββ ./k8s/frontend
βΒ Β βββ ./k8s/frontend/frontend-deployment.yaml
βΒ Β βββ ./k8s/frontend/frontend-ingress.yaml
βΒ Β βββ ./k8s/frontend/frontend-service.yaml
βββ ./trivy-ui-report-details.png
βββ ./trivy-ui-reports-list.png| Method | Endpoint | Description |
|---|---|---|
| GET | /api/ |
Root alive check |
| GET | /api/health |
Health check endpoint |
| GET | /api/metrics |
Returns number of stored reports |
| POST | /api/upload-report |
Upload Trivy file |
| POST | /api/report |
Upload Trivy JSON via body |
| GET | /api/report/{id} |
Fetch specific report details |
| GET | /api/reports |
List reports (with filters and pagination) |
| Variable | Description | Example |
|---|---|---|
TIMEZONE |
Timezone name for timestamps (pytz format) | Asia/Jerusalem |
DB_BACKEND |
filesystem, sqlite, or postgres backend |
filesystem |
FILESYSTEM_STORAGE_DIR |
Path for storing uploaded reports locally | backend/app/storage/reports |
POSTGRES_URL |
PostgreSQL connection string (if using postgres) | postgresql+asyncpg://... |
SQLITE_PATH |
SQLite database path (if using sqlite) | backend/trivy_ui.db |
Check health:
curl http://localhost:8000/api/healthExpected:
{"status": "ok"}Check metrics (total reports count):
curl http://localhost:8000/api/metricsExpected:
{"reports_count": 42}# .github/workflows/trivy-scan.yml
name: Trivy Scan and Upload
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
trivy:
name: Trivy Scan and Upload
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build Docker Image
run: |
docker build -t my-app:latest .
- name: Run Trivy scan and save JSON report
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
aquasec/trivy:latest \
image --format json -o /workspace/trivy-report.json my-app:latest
- name: Upload report to Trivy UI
run: |
curl -X POST http://trivy-ui.default.svc/report \
-H "Content-Type: application/json" \
--data-binary @trivy-report.json- Strict file upload validation: max 5MB,
.jsonor.taronly - Safe filenames via sanitization
- Automatic timezone correction for reports
- Rate limiting support (IP-based, internal)
- Frontend error boundaries
- Docker healthchecks ready
- Clear CORS policies for dev vs prod
- Strict typing across codebase (TypeScript and Python)
| Item | Description |
|---|---|
| Kubernetes Support | Helm chart + manifests for EKS, GKE, AKS |
| API Authentication | Bearer tokens, API keys or JWTs |
| Rate Limiting Enforcement | per IP rate limiter to prevent abuse |
| RBAC Roles | Admin vs Viewer accounts |
| HTTPS in Docker | Enable TLS termination in backend |
| Multi-user Mode | Associate reports to different users if authentication added |
| External Storage Support | Upload directly to AWS S3, GCS, or Azure Blob |
| Live Updates | Implement WebSocket for real-time UI refresh |
| Export Filtered Reports | Allow users to download filtered reports as JSON |
| Trivy Integration (Optional) | Trigger Trivy scans directly via UI (with credentials) |
Created with β€οΈ by Itai Ganot
- GitHub: geek-kb
MIT License β free to use, modify, distribute, and build on.

