Documentation

Everything you need to scan, score, and track your IaC security posture.

Quick Start

Install

pip install misconfig-index

Scan a directory

Point the scanner at any directory containing Terraform, Kubernetes, CloudFormation, or Dockerfile files:

misconfig scan --path ./infra

Example output:

Scanning /home/user/infra … ──────────────────────────────────────── Misconfig Score: 76/100 (Grade B) ──────────────────────────────────────── Category breakdown: networking ████████░░ 80/100 identity ███████░░░ 70/100 storage █████████░ 90/100 workload ███████░░░ 72/100 ────────────────────────────────────────

JSON output

misconfig scan --path ./infra --output json | jq '.score'

CI / GitHub Actions

Gate every pull request on your Misconfig Score in three steps:

  1. Get an API key — create an org via POST /v1/orgs, then POST /v1/orgs/{id}/keys.
  2. Add MISCONFIG_API_KEY to your repo secrets: Settings → Secrets → Actions → New repository secret.
  3. Drop this file into your repo:

.github/workflows/misconfig-index.yml

name: Misconfig Index on: push: branches: [main, master] paths: ['**.tf', '**.yaml', '**.yml', '**/Dockerfile'] pull_request: paths: ['**.tf', '**.yaml', '**.yml', '**/Dockerfile'] env: MISCONFIG_API_URL: https://api.misconfig.dev MIN_SCORE: 60 jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - run: pip install misconfig-index - name: Scan IaC env: MISCONFIG_API_KEY: ${{ secrets.MISCONFIG_API_KEY }} run: | misconfig ingest \ --path . \ --repo "${{ github.repository }}" \ --branch "${{ github.ref_name }}" \ --commit "${{ github.sha }}" \ --min-score $MIN_SCORE

Exit codes: 0 success · 1 below threshold · 2 error.

Tip: Start with MIN_SCORE: 60 (Grade C) and tighten it as your posture improves.

Score Badges

Add a live score badge to your README that updates on every push:

Markdown

![Misconfig Score](https://api.misconfig.dev/badge/YOUR_ORG/YOUR_REPO)

Badges are grade-coloured (🟢 A/B · 🟡 C · 🔴 D/F) and cached for 5 minutes.

CLI Reference

misconfig scan

OptionDefaultDescription
--path, -p.Directory to scan
--output, -otabletable or json
--saveoffPersist to local DB (requires DATABASE_URL)

misconfig ingest

OptionEnv varDescription
--path, -pDirectory to scan
--repo, -re.g. github.com/org/repo
--api-keyMISCONFIG_API_KEYAPI key (prefix mi_)
--api-urlMISCONFIG_API_URLAPI base URL
--branchMISCONFIG_BRANCHGit branch name
--commitMISCONFIG_COMMITGit commit SHA
--min-scoreExit 1 if score falls below this
--dry-runoffPrint payload without posting

misconfig serve

OptionDefaultDescription
--host127.0.0.1Bind host
--port8000Bind port
--workers1Uvicorn workers
--reloadoffAuto-reload (dev mode)

REST API

Full interactive reference at api.misconfig.dev/docs. Key endpoints:

Create an organisation

curl -X POST https://api.misconfig.dev/v1/orgs \ -H "Content-Type: application/json" \ -d '{"name": "Acme", "slug": "acme"}'

Create an API key

curl -X POST https://api.misconfig.dev/v1/orgs/{id}/keys \ -H "Content-Type: application/json" \ -d '{"name": "ci-prod"}' # Returns the full key (mi_…) — save it immediately.

Ingest a scan

curl -X POST https://api.misconfig.dev/v1/ingest \ -H "X-API-Key: mi_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "repo": "github.com/acme/infra", "branch": "main", "commit_sha": "abc123", "total_files_scanned": 42, "findings": [...] }'

Get score history

curl https://api.misconfig.dev/v1/repos/{id}/history \ -H "X-API-Key: mi_YOUR_KEY"

Scoring Explained

Each finding is weighted by severity. Weights are then normalised using a square-root of files scanned denominator so that large repos aren't unfairly penalised compared to tiny ones:

SeverityWeight
Critical10
High5
Medium2
Low1
penalty = Σ severity_weights(findings) score = clamp(0, 100 − (penalty / √files) × 3)

The × 3 scale factor is calibrated so that a single high-severity finding in a 10-file repo yields roughly 95/100 (grade A), while a repo with 10 critical findings in 50 files scores around 30/100 (grade F).

Category scores (Networking, Identity, Storage, etc.) use the same formula applied per-category and are displayed as the coloured bars in scan results.

GradeScoreMeaning
A≥ 90Excellent — minor or no findings
B≥ 75Good — a few issues to address
C≥ 60Fair — moderate security concerns
D≥ 40Poor — multiple significant findings
F< 40Critical — severe misconfigurations

The scoring engine is open source — see scanner/scoring.py for the full implementation.

Supported IaC

TypeDetected byRule categories
Terraform.tfnetworking, identity, storage, database
Kubernetes.yaml / .ymlworkload, storage, image
CloudFormationAWSTemplateFormatVersion in YAMLnetworking, storage
Dockerfilefilename Dockerfileimage

Self-Hosting

# 1. Clone and configure git clone https://github.com/cjb00/misconfig-index cd misconfig-index cp .env.example .env # edit .env → set POSTGRES_PASSWORD, ENVIRONMENT=production # 2. Start the full stack docker compose up -d # 3. Open the dashboard open http://localhost

Environment variables

VariableDefaultDescription
POSTGRES_PASSWORDrequiredPostgreSQL password
ENVIRONMENTdevelopmentdevelopment or production
API_WORKERS2Uvicorn worker count
CORS_ORIGINS*Allowed origins — restrict in production
WEB_PORT80Host port for nginx
Production tip: Set CORS_ORIGINS=https://yourdomain.com and terminate SSL with Let's Encrypt. See the launch post for a full walkthrough.