Web dashboard to receive and display LinSpec kernel hardening scan reports.
LinView = LinSpec + View.
A web dashboard purpose-built to receive, store, and display LinSpec kernel hardening scan reports — extending LinSpec from a CLI tool into a centralized security visibility layer.
Collects scan reports via REST API, stores them in SQLite, and displays aggregate statistics and per-scan details. Natively compatible with LinSpec JSON output via --webhook.
pip install -r requirements.txt
python app.pyLinView dashboard showing aggregate hardening statistics and per-scan details.
Set the LINVIEW_ADMIN_PASSWORD environment variable, then visit /admin/setup
and authenticate to create the first API key for submitting scans.
Accepts both LinSpec-native format (lowercase result) and legacy format (uppercase status). Examples:
# LinSpec-native format (from --webhook)
curl -X POST http://localhost:5000/api/scan \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-key>" \
-d '{
"hostname": "server01",
"kernel": "6.8.0",
"os": "Linux",
"checks": [
{"id": 1, "name": "aslr", "result": "pass", "category": "memory",
"current": 2, "expected": 2, "message": ""},
{"id": 2, "name": "kptr_restrict", "result": "vuln", "category": "kernel",
"current": 0, "expected": 2, "message": "kptr_restrict=0"}
]
}'
# Legacy format
curl -X POST http://localhost:5000/api/scan \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-key>" \
-d '{
"hostname": "server01",
"checks": [
{"check": "aslr", "category": "memory", "status": "PASS", "message": ""},
{"check": "kptr_restrict", "category": "kernel", "status": "VULN", "message": "bad"}
]
}'GET /api/scan/<id>/raw
| Variable | Default | Description |
|---|---|---|
PORT |
5000 |
HTTP port |
SECRET_KEY |
auto | Flask session secret |
LINVIEW_DB |
data.db |
SQLite database path |
LINVIEW_DEBUG |
false |
Enable Flask debug mode |
LINVIEW_RATE_LIMIT |
60 |
Max requests per minute per IP |
LINVIEW_ADMIN_PASSWORD |
empty | Password required to generate API keys at /admin/setup |
LINVIEW_ALLOWED_ORIGINS |
empty | Comma-separated list of allowed CORS origins (empty = CORS disabled) |
- API key is read from the
X-API-Keyheader only (query string?key=not accepted) - CORS is disabled by default; enable only specific origins via
LINVIEW_ALLOWED_ORIGINS /admin/setupis protected byLINVIEW_ADMIN_PASSWORD— never expose it without one- CSRF tokens are required for all state-changing form submissions
- Security headers (CSP, X-Frame-Options, X-Content-Type-Options, etc.) are applied to every response
- Rate limiting is in-memory per-worker (not shared across gunicorn workers)
- Always use a reverse proxy (nginx/caddy) for TLS termination
Use the bundled start.sh with gunicorn:
./start.shNever run with LINVIEW_DEBUG=true in production.
pip install pytest
python -m pytest tests/