Skip to content

jly-engineer/threat_intelligence_app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ThreatPilot

A self-hosted threat intelligence aggregation platform for MSP operators. ThreatPilot pulls from external threat intel feeds (CISA KEV, security RSS), normalizes the data, and presents a daily-updated dashboard showing active CVEs, patch recommendations, threat actor activity, and security news signal analysis.


Features

  • CVE Tracking — Ingests CISA Known Exploited Vulnerabilities (KEV) feed with severity scoring, CVSS, and EPSS data
  • Patch Queue — Automatically generates prioritized patch recommendations for your monitored software inventory
  • News Aggregation — Pulls from security RSS feeds (Bleeping Computer, Krebs on Security, Qualys Blog) with signal rating
  • Monitored Apps — Define your software stack; CVEs are automatically matched against it
  • Daily Reports — Generates PDF digests of the previous 24 hours of CVEs and news
  • Scheduled Jobs — Feed fetching, report generation, and cleanup run automatically via APScheduler
  • Dark UI — Bootstrap 5 dark theme with Chart.js dashboards and HTMX-powered interactions

Tech Stack

Layer Technology
Framework Flask 3.x
ORM Flask-SQLAlchemy / SQLAlchemy 2.0
Migrations Flask-Migrate / Alembic
Database MySQL 8.0
Auth Flask-Login + Werkzeug
Scheduler APScheduler
HTTP Client httpx
RSS Parsing feedparser
PDF Reports WeasyPrint
Frontend Jinja2 + HTMX + Chart.js + Bootstrap 5

Installation

Prerequisites

1. Clone the Repository

git clone https://github.com/jly-engineer/threat_intelligence_app.git threatpilot
cd threatpilot

2. Configure Environment

python3 -c "import secrets; print(secrets.token_hex(32))"
cp .env.example .env

Open .env and set the required values:

# Required — use a long random string (e.g. openssl rand -hex 32)
SECRET_KEY=your-secret-key-here

# Required — password for the built-in admin account
DEFAULT_ADMIN_PASSWORD=your-admin-password

# MySQL credentials
# DB_ROOT_PASSWORD and DB_PASSWORD should be strong random strings.
# Docker Compose creates the MySQL database using these values on first run,
# so there is no pre-existing database to match — just generate and paste.
# Example: python3 -c "import secrets; print(secrets.token_hex(16))"
DB_ROOT_PASSWORD=your-random-root-password
DB_PASSWORD=your-random-db-password

# DB_USER and DB_NAME can stay as defaults — they are labels, not secrets.
DB_USER=threatpilot
DB_NAME=threatpilot

# Optional — your local timezone
SCHEDULER_TIMEZONE=America/Indiana/Indianapolis

# Optional — increases NVD API rate limit
# Free key at https://nvd.nist.gov/developers/request-an-api-key
NVD_API_KEY=

DATABASE_URL is constructed automatically by Docker Compose from the DB_* vars above and does not need to be set manually.

3. Initialize Migrations (First Install Only)

docker compose run --rm app flask db init
docker compose run --rm app flask db migrate -m "initial"

4. Build and Start

docker compose up -d

This will:

  1. Pull the MySQL 8.0 image
  2. Build the ThreatPilot app image
  3. Wait for the database to be ready
  4. Run flask db upgrade to apply migrations
  5. Seed default feeds, admin user, and settings
  6. Start the app on port 8082

5. Access the App

Open your browser to http://localhost:8082 and log in with:

  • Username: admin
  • Password: the value you set for DEFAULT_ADMIN_PASSWORD

Default Feed Sources

The following feeds are seeded automatically on first run:

Feed Type Source
CISA KEV API cisa.gov
Bleeping Computer RSS bleepingcomputer.com
Krebs on Security RSS krebsonsecurity.com
Qualys Blog RSS blog.qualys.com

Additional feeds can be added from the Feed Sources page in the app.


Scheduled Jobs

Job Schedule Description
fetch_all_feeds Daily 06:00 Pull all enabled feed sources
generate_daily_report Daily 07:00 Build and save the PDF digest
cleanup_old_news Weekly Sunday Delete news items older than 90d

Useful Commands

# View live logs
docker compose logs -f app

# Stop the stack
docker compose down

# Stop and wipe all data (destructive)
docker compose down -v

# Rebuild after a code update
docker compose up -d --build

# Open a shell inside the app container
docker compose exec app bash

# Manually apply database migrations
docker compose exec app flask db upgrade

Data Persistence

Volume Contents
db_data MySQL database files
reports_output Generated PDF reports
backups Database backups

Volumes survive docker compose down. Use docker compose down -v only for a clean slate.


Upgrading

git pull
docker compose up -d --build

The entrypoint runs flask db upgrade automatically on every startup, so migrations are applied on upgrade with no additional steps.


Port Reference

Port Service
8082 ThreatPilot web UI
3306 MySQL (internal only)

Port 8081 is reserved for Profit Pilot and is not used by this application.


Troubleshooting

Tables don't exist / 500 error on login

If the app starts but crashes with Table 'threatpilot.users' doesn't exist, the database migrations did not run. This usually means the db_data volume has stale state from a previously failed startup. Wipe it and restart:

docker compose down -v
docker compose up -d --build

You should see Running upgrade -> 5aee1ee15443 in the logs before gunicorn starts. If you don't, the migration scripts are not reaching the container — verify migrations/versions/ is not excluded in .dockerignore.

MySQL is up, but ERROR 2026: TLS/SSL error

The MariaDB container does not use SSL. If the mysql CLI in the entrypoint fails with a TLS error, ensure the --ssl=FALSE flag is present in entrypoint.sh:

mysql -h "${DB_HOST}" -uroot -p"${DB_ROOT_PASSWORD}" --ssl=FALSE <<SQL

App restarts in a loop

Check logs for the root cause:

docker compose logs --tail=50 app

The most common causes are a missing or weak SECRET_KEY, a bad DATABASE_URL, or a migration failure. The entrypoint exits with a non-zero code on migration failure, which triggers Docker's restart: unless-stopped policy.


License

MIT

About

Self-hosted threat intelligence dashboard for MSP operators. Aggregates CISA KEV, NVD, and RSS security feeds into a unified view of active CVEs, patch recommendations, and threat news. Built with Flask, HTMX, and MySQL.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors