misconfig-index 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- misconfig_index-0.2.0/PKG-INFO +361 -0
- misconfig_index-0.2.0/README.md +322 -0
- misconfig_index-0.2.0/backend/__init__.py +3 -0
- misconfig_index-0.2.0/backend/auth.py +64 -0
- misconfig_index-0.2.0/backend/config.py +59 -0
- misconfig_index-0.2.0/backend/crud.py +517 -0
- misconfig_index-0.2.0/backend/deps.py +42 -0
- misconfig_index-0.2.0/backend/main.py +81 -0
- misconfig_index-0.2.0/backend/models.py +151 -0
- misconfig_index-0.2.0/backend/ratelimit.py +22 -0
- misconfig_index-0.2.0/backend/routers/__init__.py +3 -0
- misconfig_index-0.2.0/backend/routers/badge.py +108 -0
- misconfig_index-0.2.0/backend/routers/findings.py +31 -0
- misconfig_index-0.2.0/backend/routers/reports.py +156 -0
- misconfig_index-0.2.0/backend/routers/scans.py +22 -0
- misconfig_index-0.2.0/backend/routers/v1/__init__.py +0 -0
- misconfig_index-0.2.0/backend/routers/v1/benchmark.py +41 -0
- misconfig_index-0.2.0/backend/routers/v1/ingest.py +32 -0
- misconfig_index-0.2.0/backend/routers/v1/orgs.py +54 -0
- misconfig_index-0.2.0/backend/routers/v1/repos.py +91 -0
- misconfig_index-0.2.0/backend/schemas.py +258 -0
- misconfig_index-0.2.0/misconfig_index.egg-info/PKG-INFO +361 -0
- misconfig_index-0.2.0/misconfig_index.egg-info/SOURCES.txt +43 -0
- misconfig_index-0.2.0/misconfig_index.egg-info/dependency_links.txt +1 -0
- misconfig_index-0.2.0/misconfig_index.egg-info/entry_points.txt +2 -0
- misconfig_index-0.2.0/misconfig_index.egg-info/requires.txt +20 -0
- misconfig_index-0.2.0/misconfig_index.egg-info/top_level.txt +2 -0
- misconfig_index-0.2.0/pyproject.toml +75 -0
- misconfig_index-0.2.0/scanner/__init__.py +3 -0
- misconfig_index-0.2.0/scanner/__main__.py +11 -0
- misconfig_index-0.2.0/scanner/ci_ingest.py +162 -0
- misconfig_index-0.2.0/scanner/cli.py +320 -0
- misconfig_index-0.2.0/scanner/cmd.py +449 -0
- misconfig_index-0.2.0/scanner/config.py +18 -0
- misconfig_index-0.2.0/scanner/github_client.py +29 -0
- misconfig_index-0.2.0/scanner/loader.py +17 -0
- misconfig_index-0.2.0/scanner/rules/__init__.py +4 -0
- misconfig_index-0.2.0/scanner/rules/base.py +41 -0
- misconfig_index-0.2.0/scanner/rules/cloudformation.py +12 -0
- misconfig_index-0.2.0/scanner/rules/dockerfile.py +12 -0
- misconfig_index-0.2.0/scanner/rules/kubernetes.py +268 -0
- misconfig_index-0.2.0/scanner/rules/terraform.py +436 -0
- misconfig_index-0.2.0/scanner/scanner.py +41 -0
- misconfig_index-0.2.0/scanner/scoring.py +165 -0
- misconfig_index-0.2.0/setup.cfg +4 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: misconfig-index
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: IaC misconfiguration scanner — score, track, and benchmark cloud security posture.
|
|
5
|
+
Author-email: Misconfig Index <hello@misconfig.dev>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://misconfig.dev
|
|
8
|
+
Project-URL: Repository, https://github.com/cjb00/misconfig-index
|
|
9
|
+
Project-URL: Documentation, https://misconfig.dev/docs
|
|
10
|
+
Project-URL: Bug Tracker, https://github.com/cjb00/misconfig-index/issues
|
|
11
|
+
Keywords: security,iac,terraform,kubernetes,devsecops,misconfiguration
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: click>=8.0.0
|
|
22
|
+
Requires-Dist: pyyaml>=6.0
|
|
23
|
+
Requires-Dist: requests>=2.31.0
|
|
24
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
25
|
+
Provides-Extra: server
|
|
26
|
+
Requires-Dist: fastapi>=0.109.0; extra == "server"
|
|
27
|
+
Requires-Dist: uvicorn[standard]>=0.24.0; extra == "server"
|
|
28
|
+
Requires-Dist: sqlalchemy>=2.0.0; extra == "server"
|
|
29
|
+
Requires-Dist: pydantic>=2.5.0; extra == "server"
|
|
30
|
+
Requires-Dist: pydantic-settings>=2.0.0; extra == "server"
|
|
31
|
+
Requires-Dist: psycopg2-binary>=2.9.0; extra == "server"
|
|
32
|
+
Requires-Dist: alembic>=1.13.0; extra == "server"
|
|
33
|
+
Requires-Dist: slowapi>=0.1.9; extra == "server"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
36
|
+
Requires-Dist: httpx>=0.25.0; extra == "dev"
|
|
37
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
39
|
+
|
|
40
|
+
# Misconfig Index
|
|
41
|
+
|
|
42
|
+
**IaC misconfiguration scanner — score, track, and benchmark cloud security posture.**
|
|
43
|
+
|
|
44
|
+
[](https://misconfig.dev)
|
|
45
|
+
[](https://pypi.org/project/misconfig-index/)
|
|
46
|
+
[](https://python.org)
|
|
47
|
+
[](LICENSE)
|
|
48
|
+
|
|
49
|
+
Misconfig Index scans Terraform, Kubernetes, CloudFormation, and Dockerfile IaC, applies rule packs, and converts findings into a single weighted **Misconfig Score** (0–100). Scores are tracked over time so you can see your security posture improving — or catch regressions before they reach production.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- **Instant score** — one command to scan any IaC directory and get a weighted grade (A–F)
|
|
56
|
+
- **Category breakdown** — per-domain scores: networking, identity, storage, workload, image
|
|
57
|
+
- **Trend tracking** — every scan is stored; see your score history over time
|
|
58
|
+
- **CI gate** — fail a build if score drops below your threshold
|
|
59
|
+
- **Live badges** — embed your current score in any README
|
|
60
|
+
- **REST API** — ingest scans from any tool, query history, benchmark against the field
|
|
61
|
+
- **Self-hostable** — one `docker compose up` to run the full stack with PostgreSQL
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Quick start
|
|
66
|
+
|
|
67
|
+
### Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install misconfig-index
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Scan a directory
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
misconfig scan --path ./infra
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
Scanning /home/user/infra …
|
|
81
|
+
|
|
82
|
+
────────────────────────────────────────
|
|
83
|
+
Misconfig Score: 76/100 (Grade B)
|
|
84
|
+
────────────────────────────────────────
|
|
85
|
+
Category breakdown:
|
|
86
|
+
networking ████████░░ 80/100
|
|
87
|
+
identity ███████░░░ 70/100
|
|
88
|
+
storage █████████░ 90/100
|
|
89
|
+
workload ███████░░░ 72/100
|
|
90
|
+
────────────────────────────────────────
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Get JSON output (for scripting)
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
misconfig scan --path ./infra --output json | jq '.score'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## CI / GitHub Actions
|
|
102
|
+
|
|
103
|
+
Gate every pull request on your Misconfig Score in three steps:
|
|
104
|
+
|
|
105
|
+
**1. Add `MISCONFIG_API_KEY` to your repository secrets.**
|
|
106
|
+
|
|
107
|
+
**2. Drop this workflow into `.github/workflows/misconfig-index.yml`:**
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
name: Misconfig Index
|
|
111
|
+
|
|
112
|
+
on:
|
|
113
|
+
push:
|
|
114
|
+
paths: ['**.tf', '**.yaml', '**.yml', '**/Dockerfile']
|
|
115
|
+
pull_request:
|
|
116
|
+
paths: ['**.tf', '**.yaml', '**.yml', '**/Dockerfile']
|
|
117
|
+
|
|
118
|
+
env:
|
|
119
|
+
MIN_SCORE: 60
|
|
120
|
+
|
|
121
|
+
jobs:
|
|
122
|
+
scan:
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
steps:
|
|
125
|
+
- uses: actions/checkout@v4
|
|
126
|
+
- uses: actions/setup-python@v5
|
|
127
|
+
with:
|
|
128
|
+
python-version: '3.11'
|
|
129
|
+
- run: pip install misconfig-index
|
|
130
|
+
- name: Scan IaC
|
|
131
|
+
env:
|
|
132
|
+
MISCONFIG_API_KEY: ${{ secrets.MISCONFIG_API_KEY }}
|
|
133
|
+
run: |
|
|
134
|
+
misconfig ingest \
|
|
135
|
+
--path . \
|
|
136
|
+
--repo "${{ github.repository }}" \
|
|
137
|
+
--branch "${{ github.ref_name }}" \
|
|
138
|
+
--commit "${{ github.sha }}" \
|
|
139
|
+
--min-score $MIN_SCORE
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**3. Adjust `MIN_SCORE` to your quality gate.**
|
|
143
|
+
|
|
144
|
+
The scanner exits `0` on success, `1` if below threshold, `2` on error — GitHub will fail the check automatically.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Score badges
|
|
149
|
+
|
|
150
|
+
Add a live score badge to your README that updates on every push:
|
|
151
|
+
|
|
152
|
+
```markdown
|
|
153
|
+

|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Badges are grade-coloured (🟢 A, 🟡 C, 🔴 F) and cached for 5 minutes.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## CLI reference
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
Usage: misconfig [OPTIONS] COMMAND [ARGS]...
|
|
164
|
+
|
|
165
|
+
Misconfig Index — IaC misconfiguration scanner & API.
|
|
166
|
+
|
|
167
|
+
Commands:
|
|
168
|
+
scan Scan IaC files and print the misconfiguration score.
|
|
169
|
+
ingest Scan and push results to the Misconfig Index API.
|
|
170
|
+
serve Start the Misconfig Index API server.
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### `misconfig scan`
|
|
174
|
+
|
|
175
|
+
| Option | Default | Description |
|
|
176
|
+
|--------|---------|-------------|
|
|
177
|
+
| `--path`, `-p` | `.` | Directory to scan |
|
|
178
|
+
| `--output`, `-o` | `table` | `table` or `json` |
|
|
179
|
+
| `--save` | off | Persist to local DB (requires `DATABASE_URL`) |
|
|
180
|
+
|
|
181
|
+
### `misconfig ingest`
|
|
182
|
+
|
|
183
|
+
| Option | Env var | Description |
|
|
184
|
+
|--------|---------|-------------|
|
|
185
|
+
| `--path`, `-p` | — | Directory to scan |
|
|
186
|
+
| `--repo`, `-r` | — | Repo identifier (e.g. `github.com/org/repo`) |
|
|
187
|
+
| `--api-key` | `MISCONFIG_API_KEY` | API key |
|
|
188
|
+
| `--api-url` | `MISCONFIG_API_URL` | API base URL |
|
|
189
|
+
| `--branch` | `MISCONFIG_BRANCH` | Git branch |
|
|
190
|
+
| `--commit` | `MISCONFIG_COMMIT` | Git commit SHA |
|
|
191
|
+
| `--min-score` | — | Fail if score is below this value |
|
|
192
|
+
| `--dry-run` | off | Print payload without posting |
|
|
193
|
+
|
|
194
|
+
### `misconfig serve`
|
|
195
|
+
|
|
196
|
+
| Option | Default | Description |
|
|
197
|
+
|--------|---------|-------------|
|
|
198
|
+
| `--host` | `127.0.0.1` | Bind host |
|
|
199
|
+
| `--port` | `8000` | Bind port |
|
|
200
|
+
| `--workers` | `1` | Worker processes |
|
|
201
|
+
| `--reload` | off | Auto-reload (dev) |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## REST API
|
|
206
|
+
|
|
207
|
+
Interactive docs are available at `/docs` when the server is running.
|
|
208
|
+
|
|
209
|
+
### Create an organisation
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
curl -X POST https://api.misconfig.dev/v1/orgs \
|
|
213
|
+
-H "Content-Type: application/json" \
|
|
214
|
+
-d '{"name": "Acme", "slug": "acme"}'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Create an API key
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
curl -X POST https://api.misconfig.dev/v1/orgs/{id}/keys \
|
|
221
|
+
-H "Content-Type: application/json" \
|
|
222
|
+
-d '{"name": "ci-prod"}'
|
|
223
|
+
# Returns the full key (mi_…) once — save it to your secrets manager now.
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Ingest a scan
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
curl -X POST https://api.misconfig.dev/v1/ingest \
|
|
230
|
+
-H "X-API-Key: mi_YOUR_KEY" \
|
|
231
|
+
-H "Content-Type: application/json" \
|
|
232
|
+
-d '{
|
|
233
|
+
"repo": "github.com/acme/infra",
|
|
234
|
+
"branch": "main",
|
|
235
|
+
"commit_sha": "abc123",
|
|
236
|
+
"total_files_scanned": 42,
|
|
237
|
+
"findings": [
|
|
238
|
+
{
|
|
239
|
+
"rule_id": "TF_OPEN_SG_0_0_0_0",
|
|
240
|
+
"file_path": "network/sg.tf",
|
|
241
|
+
"file_type": "terraform",
|
|
242
|
+
"line_start": 14,
|
|
243
|
+
"snippet": "cidr_blocks = [\"0.0.0.0/0\"]"
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
}'
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Get repo score history
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
curl https://api.misconfig.dev/v1/repos/{id}/history \
|
|
253
|
+
-H "X-API-Key: mi_YOUR_KEY"
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Self-hosting
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# 1. Set your database password (required)
|
|
262
|
+
cp .env.example .env
|
|
263
|
+
# edit .env → set POSTGRES_PASSWORD
|
|
264
|
+
|
|
265
|
+
# 2. Start the full stack
|
|
266
|
+
docker compose up -d
|
|
267
|
+
|
|
268
|
+
# 3. Open the dashboard
|
|
269
|
+
open http://localhost
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
| Service | Image | Role |
|
|
273
|
+
|---------|-------|------|
|
|
274
|
+
| `db` | `postgres:16-alpine` | Persistent data store |
|
|
275
|
+
| `api` | Built from `Dockerfile` | FastAPI backend (auto-creates schema on first boot) |
|
|
276
|
+
| `web` | `nginx:1.27-alpine` | Frontend static files + `/api` reverse proxy |
|
|
277
|
+
|
|
278
|
+
### Environment variables
|
|
279
|
+
|
|
280
|
+
| Variable | Default | Description |
|
|
281
|
+
|----------|---------|-------------|
|
|
282
|
+
| `POSTGRES_PASSWORD` | *(required)* | PostgreSQL password |
|
|
283
|
+
| `DATABASE_URL` | `sqlite:///./misconfig_index.db` | Override for SQLite dev |
|
|
284
|
+
| `ENVIRONMENT` | `development` | `development` or `production` |
|
|
285
|
+
| `API_WORKERS` | `2` | uvicorn worker count |
|
|
286
|
+
| `CORS_ORIGINS` | `*` | Allowed origins (set to your domain in prod) |
|
|
287
|
+
| `WEB_PORT` | `80` | Host port for nginx |
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Scoring
|
|
292
|
+
|
|
293
|
+
Findings are weighted by severity, normalised by files scanned:
|
|
294
|
+
|
|
295
|
+
| Severity | Weight |
|
|
296
|
+
|----------|--------|
|
|
297
|
+
| Critical | 10 |
|
|
298
|
+
| High | 5 |
|
|
299
|
+
| Medium | 2 |
|
|
300
|
+
| Low | 1 |
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
score = clamp(0, 100 − (Σ weights / files_scanned) × 10)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
| Grade | Score range |
|
|
307
|
+
|-------|-------------|
|
|
308
|
+
| A | ≥ 90 |
|
|
309
|
+
| B | ≥ 75 |
|
|
310
|
+
| C | ≥ 60 |
|
|
311
|
+
| D | ≥ 40 |
|
|
312
|
+
| F | < 40 |
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Supported IaC
|
|
317
|
+
|
|
318
|
+
| Type | Detected by | Rule categories |
|
|
319
|
+
|------|-------------|-----------------|
|
|
320
|
+
| Terraform | `.tf` extension | networking, identity, storage, database |
|
|
321
|
+
| Kubernetes | `.yaml`/`.yml` | workload, storage, image |
|
|
322
|
+
| CloudFormation | `AWSTemplateFormatVersion` in YAML | networking, storage |
|
|
323
|
+
| Dockerfile | filename `Dockerfile` | image |
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Development
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
git clone https://github.com/misconfig-index/misconfig-index
|
|
331
|
+
cd misconfig-index
|
|
332
|
+
python -m venv .venv && source .venv/bin/activate
|
|
333
|
+
pip install -e .
|
|
334
|
+
|
|
335
|
+
# Scan the included sample fixtures
|
|
336
|
+
misconfig scan --path samples/
|
|
337
|
+
|
|
338
|
+
# Start the API with hot-reload
|
|
339
|
+
misconfig serve --reload
|
|
340
|
+
|
|
341
|
+
# Run with the Python module path as well
|
|
342
|
+
python -m scanner scan --path samples/
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Contributing
|
|
348
|
+
|
|
349
|
+
Contributions are welcome. The highest-impact areas right now:
|
|
350
|
+
|
|
351
|
+
- **New rules** — add a `.py` to `scanner/rules/` following the `Rule` base class pattern
|
|
352
|
+
- **Rule improvements** — reduce false positives in existing regex patterns
|
|
353
|
+
- **New IaC types** — Pulumi, Ansible, Bicep, ARM templates
|
|
354
|
+
|
|
355
|
+
Please open an issue before submitting large PRs.
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## License
|
|
360
|
+
|
|
361
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# Misconfig Index
|
|
2
|
+
|
|
3
|
+
**IaC misconfiguration scanner — score, track, and benchmark cloud security posture.**
|
|
4
|
+
|
|
5
|
+
[](https://misconfig.dev)
|
|
6
|
+
[](https://pypi.org/project/misconfig-index/)
|
|
7
|
+
[](https://python.org)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
Misconfig Index scans Terraform, Kubernetes, CloudFormation, and Dockerfile IaC, applies rule packs, and converts findings into a single weighted **Misconfig Score** (0–100). Scores are tracked over time so you can see your security posture improving — or catch regressions before they reach production.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Instant score** — one command to scan any IaC directory and get a weighted grade (A–F)
|
|
17
|
+
- **Category breakdown** — per-domain scores: networking, identity, storage, workload, image
|
|
18
|
+
- **Trend tracking** — every scan is stored; see your score history over time
|
|
19
|
+
- **CI gate** — fail a build if score drops below your threshold
|
|
20
|
+
- **Live badges** — embed your current score in any README
|
|
21
|
+
- **REST API** — ingest scans from any tool, query history, benchmark against the field
|
|
22
|
+
- **Self-hostable** — one `docker compose up` to run the full stack with PostgreSQL
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
### Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install misconfig-index
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Scan a directory
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
misconfig scan --path ./infra
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Scanning /home/user/infra …
|
|
42
|
+
|
|
43
|
+
────────────────────────────────────────
|
|
44
|
+
Misconfig Score: 76/100 (Grade B)
|
|
45
|
+
────────────────────────────────────────
|
|
46
|
+
Category breakdown:
|
|
47
|
+
networking ████████░░ 80/100
|
|
48
|
+
identity ███████░░░ 70/100
|
|
49
|
+
storage █████████░ 90/100
|
|
50
|
+
workload ███████░░░ 72/100
|
|
51
|
+
────────────────────────────────────────
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Get JSON output (for scripting)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
misconfig scan --path ./infra --output json | jq '.score'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## CI / GitHub Actions
|
|
63
|
+
|
|
64
|
+
Gate every pull request on your Misconfig Score in three steps:
|
|
65
|
+
|
|
66
|
+
**1. Add `MISCONFIG_API_KEY` to your repository secrets.**
|
|
67
|
+
|
|
68
|
+
**2. Drop this workflow into `.github/workflows/misconfig-index.yml`:**
|
|
69
|
+
|
|
70
|
+
```yaml
|
|
71
|
+
name: Misconfig Index
|
|
72
|
+
|
|
73
|
+
on:
|
|
74
|
+
push:
|
|
75
|
+
paths: ['**.tf', '**.yaml', '**.yml', '**/Dockerfile']
|
|
76
|
+
pull_request:
|
|
77
|
+
paths: ['**.tf', '**.yaml', '**.yml', '**/Dockerfile']
|
|
78
|
+
|
|
79
|
+
env:
|
|
80
|
+
MIN_SCORE: 60
|
|
81
|
+
|
|
82
|
+
jobs:
|
|
83
|
+
scan:
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/checkout@v4
|
|
87
|
+
- uses: actions/setup-python@v5
|
|
88
|
+
with:
|
|
89
|
+
python-version: '3.11'
|
|
90
|
+
- run: pip install misconfig-index
|
|
91
|
+
- name: Scan IaC
|
|
92
|
+
env:
|
|
93
|
+
MISCONFIG_API_KEY: ${{ secrets.MISCONFIG_API_KEY }}
|
|
94
|
+
run: |
|
|
95
|
+
misconfig ingest \
|
|
96
|
+
--path . \
|
|
97
|
+
--repo "${{ github.repository }}" \
|
|
98
|
+
--branch "${{ github.ref_name }}" \
|
|
99
|
+
--commit "${{ github.sha }}" \
|
|
100
|
+
--min-score $MIN_SCORE
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**3. Adjust `MIN_SCORE` to your quality gate.**
|
|
104
|
+
|
|
105
|
+
The scanner exits `0` on success, `1` if below threshold, `2` on error — GitHub will fail the check automatically.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Score badges
|
|
110
|
+
|
|
111
|
+
Add a live score badge to your README that updates on every push:
|
|
112
|
+
|
|
113
|
+
```markdown
|
|
114
|
+

|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Badges are grade-coloured (🟢 A, 🟡 C, 🔴 F) and cached for 5 minutes.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## CLI reference
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
Usage: misconfig [OPTIONS] COMMAND [ARGS]...
|
|
125
|
+
|
|
126
|
+
Misconfig Index — IaC misconfiguration scanner & API.
|
|
127
|
+
|
|
128
|
+
Commands:
|
|
129
|
+
scan Scan IaC files and print the misconfiguration score.
|
|
130
|
+
ingest Scan and push results to the Misconfig Index API.
|
|
131
|
+
serve Start the Misconfig Index API server.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `misconfig scan`
|
|
135
|
+
|
|
136
|
+
| Option | Default | Description |
|
|
137
|
+
|--------|---------|-------------|
|
|
138
|
+
| `--path`, `-p` | `.` | Directory to scan |
|
|
139
|
+
| `--output`, `-o` | `table` | `table` or `json` |
|
|
140
|
+
| `--save` | off | Persist to local DB (requires `DATABASE_URL`) |
|
|
141
|
+
|
|
142
|
+
### `misconfig ingest`
|
|
143
|
+
|
|
144
|
+
| Option | Env var | Description |
|
|
145
|
+
|--------|---------|-------------|
|
|
146
|
+
| `--path`, `-p` | — | Directory to scan |
|
|
147
|
+
| `--repo`, `-r` | — | Repo identifier (e.g. `github.com/org/repo`) |
|
|
148
|
+
| `--api-key` | `MISCONFIG_API_KEY` | API key |
|
|
149
|
+
| `--api-url` | `MISCONFIG_API_URL` | API base URL |
|
|
150
|
+
| `--branch` | `MISCONFIG_BRANCH` | Git branch |
|
|
151
|
+
| `--commit` | `MISCONFIG_COMMIT` | Git commit SHA |
|
|
152
|
+
| `--min-score` | — | Fail if score is below this value |
|
|
153
|
+
| `--dry-run` | off | Print payload without posting |
|
|
154
|
+
|
|
155
|
+
### `misconfig serve`
|
|
156
|
+
|
|
157
|
+
| Option | Default | Description |
|
|
158
|
+
|--------|---------|-------------|
|
|
159
|
+
| `--host` | `127.0.0.1` | Bind host |
|
|
160
|
+
| `--port` | `8000` | Bind port |
|
|
161
|
+
| `--workers` | `1` | Worker processes |
|
|
162
|
+
| `--reload` | off | Auto-reload (dev) |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## REST API
|
|
167
|
+
|
|
168
|
+
Interactive docs are available at `/docs` when the server is running.
|
|
169
|
+
|
|
170
|
+
### Create an organisation
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
curl -X POST https://api.misconfig.dev/v1/orgs \
|
|
174
|
+
-H "Content-Type: application/json" \
|
|
175
|
+
-d '{"name": "Acme", "slug": "acme"}'
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Create an API key
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
curl -X POST https://api.misconfig.dev/v1/orgs/{id}/keys \
|
|
182
|
+
-H "Content-Type: application/json" \
|
|
183
|
+
-d '{"name": "ci-prod"}'
|
|
184
|
+
# Returns the full key (mi_…) once — save it to your secrets manager now.
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Ingest a scan
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
curl -X POST https://api.misconfig.dev/v1/ingest \
|
|
191
|
+
-H "X-API-Key: mi_YOUR_KEY" \
|
|
192
|
+
-H "Content-Type: application/json" \
|
|
193
|
+
-d '{
|
|
194
|
+
"repo": "github.com/acme/infra",
|
|
195
|
+
"branch": "main",
|
|
196
|
+
"commit_sha": "abc123",
|
|
197
|
+
"total_files_scanned": 42,
|
|
198
|
+
"findings": [
|
|
199
|
+
{
|
|
200
|
+
"rule_id": "TF_OPEN_SG_0_0_0_0",
|
|
201
|
+
"file_path": "network/sg.tf",
|
|
202
|
+
"file_type": "terraform",
|
|
203
|
+
"line_start": 14,
|
|
204
|
+
"snippet": "cidr_blocks = [\"0.0.0.0/0\"]"
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Get repo score history
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
curl https://api.misconfig.dev/v1/repos/{id}/history \
|
|
214
|
+
-H "X-API-Key: mi_YOUR_KEY"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Self-hosting
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# 1. Set your database password (required)
|
|
223
|
+
cp .env.example .env
|
|
224
|
+
# edit .env → set POSTGRES_PASSWORD
|
|
225
|
+
|
|
226
|
+
# 2. Start the full stack
|
|
227
|
+
docker compose up -d
|
|
228
|
+
|
|
229
|
+
# 3. Open the dashboard
|
|
230
|
+
open http://localhost
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
| Service | Image | Role |
|
|
234
|
+
|---------|-------|------|
|
|
235
|
+
| `db` | `postgres:16-alpine` | Persistent data store |
|
|
236
|
+
| `api` | Built from `Dockerfile` | FastAPI backend (auto-creates schema on first boot) |
|
|
237
|
+
| `web` | `nginx:1.27-alpine` | Frontend static files + `/api` reverse proxy |
|
|
238
|
+
|
|
239
|
+
### Environment variables
|
|
240
|
+
|
|
241
|
+
| Variable | Default | Description |
|
|
242
|
+
|----------|---------|-------------|
|
|
243
|
+
| `POSTGRES_PASSWORD` | *(required)* | PostgreSQL password |
|
|
244
|
+
| `DATABASE_URL` | `sqlite:///./misconfig_index.db` | Override for SQLite dev |
|
|
245
|
+
| `ENVIRONMENT` | `development` | `development` or `production` |
|
|
246
|
+
| `API_WORKERS` | `2` | uvicorn worker count |
|
|
247
|
+
| `CORS_ORIGINS` | `*` | Allowed origins (set to your domain in prod) |
|
|
248
|
+
| `WEB_PORT` | `80` | Host port for nginx |
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Scoring
|
|
253
|
+
|
|
254
|
+
Findings are weighted by severity, normalised by files scanned:
|
|
255
|
+
|
|
256
|
+
| Severity | Weight |
|
|
257
|
+
|----------|--------|
|
|
258
|
+
| Critical | 10 |
|
|
259
|
+
| High | 5 |
|
|
260
|
+
| Medium | 2 |
|
|
261
|
+
| Low | 1 |
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
score = clamp(0, 100 − (Σ weights / files_scanned) × 10)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
| Grade | Score range |
|
|
268
|
+
|-------|-------------|
|
|
269
|
+
| A | ≥ 90 |
|
|
270
|
+
| B | ≥ 75 |
|
|
271
|
+
| C | ≥ 60 |
|
|
272
|
+
| D | ≥ 40 |
|
|
273
|
+
| F | < 40 |
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Supported IaC
|
|
278
|
+
|
|
279
|
+
| Type | Detected by | Rule categories |
|
|
280
|
+
|------|-------------|-----------------|
|
|
281
|
+
| Terraform | `.tf` extension | networking, identity, storage, database |
|
|
282
|
+
| Kubernetes | `.yaml`/`.yml` | workload, storage, image |
|
|
283
|
+
| CloudFormation | `AWSTemplateFormatVersion` in YAML | networking, storage |
|
|
284
|
+
| Dockerfile | filename `Dockerfile` | image |
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Development
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
git clone https://github.com/misconfig-index/misconfig-index
|
|
292
|
+
cd misconfig-index
|
|
293
|
+
python -m venv .venv && source .venv/bin/activate
|
|
294
|
+
pip install -e .
|
|
295
|
+
|
|
296
|
+
# Scan the included sample fixtures
|
|
297
|
+
misconfig scan --path samples/
|
|
298
|
+
|
|
299
|
+
# Start the API with hot-reload
|
|
300
|
+
misconfig serve --reload
|
|
301
|
+
|
|
302
|
+
# Run with the Python module path as well
|
|
303
|
+
python -m scanner scan --path samples/
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Contributing
|
|
309
|
+
|
|
310
|
+
Contributions are welcome. The highest-impact areas right now:
|
|
311
|
+
|
|
312
|
+
- **New rules** — add a `.py` to `scanner/rules/` following the `Rule` base class pattern
|
|
313
|
+
- **Rule improvements** — reduce false positives in existing regex patterns
|
|
314
|
+
- **New IaC types** — Pulumi, Ansible, Bicep, ARM templates
|
|
315
|
+
|
|
316
|
+
Please open an issue before submitting large PRs.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## License
|
|
321
|
+
|
|
322
|
+
MIT — see [LICENSE](LICENSE).
|