Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/morwn/github-threat-detector/llms.txt

Use this file to discover all available pages before exploring further.

GitHub Threat Detector is a self-hosted CLI tool with no cloud dependencies — all you need is Python 3.10+, a running PostgreSQL instance, and a GitHub Personal Access Token. The steps below walk you through cloning the project, wiring up your environment, and producing your first findings report.
Your GitHub PAT must have the repo and read:org scopes. The repo scope is required to read events and workflow files from repositories; read:org is required to collect organization membership events that power the member-added-suspicious detector.
1

Clone the repo and install dependencies

Clone the repository and install the Python dependencies into your environment.
git clone https://github.com/morwn/github-threat-detector.git
cd github-threat-detector
pip install -r requirements.txt
The dependency set is intentionally minimal:
requirements.txt
httpx==0.27.2
psycopg2-binary==2.9.11
click==8.1.7
python-dotenv==1.0.1
rich==13.9.4
httpx handles all GitHub API calls with async-ready connection pooling, psycopg2-binary is the self-contained PostgreSQL driver, click powers the CLI, python-dotenv loads your .env file, and rich renders the terminal report table.
2

Set up environment variables

Copy the example environment file and fill in your credentials.
cp .env.example .env
Open .env in your editor. The file ships with the following defaults:
.env.example
GITHUB_TOKEN=ghp_your_personal_access_token_here
DATABASE_URL=postgresql://postgres@localhost:5432/threat_detector
GITHUB_REPOS=aquasecurity/trivy,kubernetes/kubernetes
  • GITHUB_TOKEN — your GitHub PAT with repo and read:org scopes.
  • DATABASE_URL — a standard PostgreSQL connection string. The database must already exist; the schema is applied automatically by init-db.
  • GITHUB_REPOS — a comma-separated list of owner/repo targets used as the default when --repos is not passed on the command line.
Setting GITHUB_REPOS in your .env file means you never have to pass --repos on every collect, analyze, or report invocation. The CLI reads the env var as the default repo list whenever the flag is omitted.
3

Initialize the database schema

Apply the PostgreSQL schema. This creates the events, workflow_runs, releases, contributors, workflow_files, commits, and findings tables if they do not already exist. The command is idempotent — safe to run more than once.
python cli.py init-db
Schema applied.
4

Collect GitHub events

Run the collector against a repository. The flags below enable all data sources, giving the analyzers the richest possible signal.
python cli.py collect \
  --repos aquasecurity/trivy \
  --actions \
  --releases \
  --contributors \
  --workflow-files \
  --commits
FlagData collected
(default)GitHub Events API (pushes, PRs, forks, member changes…)
--actionsGitHub Actions workflow run history
--releasesRepository releases and their publishing actors
--contributorsHistorical contributor list (improves release-actor-anomaly accuracy)
--workflow-filesRaw workflow YAML (enables ai-workflow-unsafe-input analysis)
--commitsCommit metadata for default-branch pushes (enables commit-date-gap and null-committer-email)
You can also collect from an entire GitHub organization using --orgs:
python cli.py collect --orgs aquasecurity
5

Run the analyzers

Execute all 15 heuristic analyzers against the collected data. Findings are written to the findings table in PostgreSQL.
python cli.py analyze
To run only a specific subset of rules, pass a comma-separated list of rule IDs:
python cli.py analyze --rules pr-target-abuse,workflow-file-change,release-actor-anomaly
6

View the report

Display findings in the terminal. Use --severity, --repos, --since, and --format to narrow results to exactly what you need.
python cli.py report --severity critical,high
Example output:
╭────┬──────────┬────────────────────────────────┬───────────────────────┬──────────────────────┬──────────────────────────────────────────────────────────────────────────┬─────────────────────╮
│ ID │ Severity │ Rule                           │ Repo                  │ Actor                │ Description                                                              │ When                │
├────┼──────────┼────────────────────────────────┼───────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────┼─────────────────────┤
│ 12 │ critical │ pr-target-abuse                │ aquasecurity/trivy    │ dependabot[bot]      │ pull_request_target workflow triggered from fork branch feat/cve-update  │ 2024-11-03 08:14:22 │
│ 11 │ critical │ release-actor-anomaly          │ aquasecurity/trivy    │ ghost-patcher99      │ Release v0.57.1 published by actor with no prior release history          │ 2024-11-02 23:51:07 │
│  9 │ high     │ workflow-file-change           │ aquasecurity/trivy    │ ghost-patcher99      │ .github/workflows/release.yml modified by first-time contributor          │ 2024-11-02 23:48:33 │
│  7 │ high     │ force-push-default             │ aquasecurity/trivy    │ ghost-patcher99      │ Force push to default branch main detected                                │ 2024-11-02 23:47:19 │
│  5 │ high     │ new-actor-ci-change            │ kubernetes/kubernetes │ new-contrib-7f3a     │ Actor with < 30 days account age modified CI workflow file               │ 2024-11-01 14:22:48 │
╰────┴──────────┴────────────────────────────────┴───────────────────────┴──────────────────────┴──────────────────────────────────────────────────────────────────────────┴─────────────────────╯

Total: 5 finding(s)
Filter by a single repository using --repos:
python cli.py report --repos aquasecurity/trivy --severity high,critical
Export findings as JSON for integration with SIEM tools or alerting pipelines:
python cli.py report --format json
[
  {
    "id": 12,
    "severity": "critical",
    "rule_id": "pr-target-abuse",
    "repo_name": "aquasecurity/trivy",
    "actor_login": "dependabot[bot]",
    "description": "pull_request_target workflow triggered from fork branch feat/cve-update",
    "created_at": "2024-11-03 08:14:22"
  }
]
You can also filter by time window using --since:
# Findings from the last 24 hours
python cli.py report --severity critical,high --since 24h

# Findings since a specific date
python cli.py report --since 2024-11-01T00:00:00

Next Steps

CLI Reference

Explore every flag and option for collect, analyze, report, and init-db.

Detectors Overview

Understand the logic behind each of the 15 detection rules and how to tune thresholds.

Build docs developers (and LLMs) love