{"id":51530973,"url":"https://github.com/maximeroucher/heimdall","last_synced_at":"2026-07-09T02:30:35.091Z","repository":{"id":369375904,"uuid":"1289574596","full_name":"maximeroucher/heimdall","owner":"maximeroucher","description":"Self-discovering OWASP Top-10 pentest library for FastAPI apps","archived":false,"fork":false,"pushed_at":"2026-07-05T00:11:13.000Z","size":483,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-05T02:08:47.474Z","etag":null,"topics":["dast","fastapi","owasp","pentest","security"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maximeroucher.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-04T23:44:38.000Z","updated_at":"2026-07-05T00:11:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/maximeroucher/heimdall","commit_stats":null,"previous_names":["maximeroucher/heimdall"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/maximeroucher/heimdall","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximeroucher%2Fheimdall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximeroucher%2Fheimdall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximeroucher%2Fheimdall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximeroucher%2Fheimdall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maximeroucher","download_url":"https://codeload.github.com/maximeroucher/heimdall/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maximeroucher%2Fheimdall/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35284875,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dast","fastapi","owasp","pentest","security"],"created_at":"2026-07-09T02:30:34.542Z","updated_at":"2026-07-09T02:30:35.080Z","avatar_url":"https://github.com/maximeroucher.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Heimdall\n\n**A self-discovering OWASP Top-10 pentest library for FastAPI applications.**\n\n\u003e Heimdall is the watchman of the Norse gods — he sees everything that approaches.\n\u003e Point Heimdall at a running FastAPI app and it *sees the app*: it learns the\n\u003e routes, the auth shape, and (optionally) the signing secrets, then runs the\n\u003e OWASP Top-10 exploit modules against the live target and writes a report.\n\nIt grew out of a hand-written pentest of one app and was generalized so the same\nmethodology runs against **any** FastAPI service with near-zero per-app wiring.\n\n---\n\n## Why it's generic\n\nMost pentest scripts hard-code paths (`/core/auth/login`), credentials, and the\nvictim's data model. Heimdall doesn't. It **discovers** them:\n\n| Concern | How Heimdall learns it |\n|---|---|\n| Every route, method, required-auth, params, body schema | `GET /openapi.json` (every FastAPI app serves it) |\n| Login shape (JSON vs OAuth2 form vs `simple_token`) | Heuristic scoring over the OpenAPI operations |\n| Register / \"current user\" endpoints | Same |\n| Auth transport — bearer token **or** cookie-session | `securitySchemes` + the live login response (302/204 + `Set-Cookie` → cookie jar; `Authorization` scheme → token) |\n| JWT algorithm \u0026 claim shape | Decodes a real token minted through the login flow |\n| Candidate signing secrets, dependency stack | Optional white-box scan of the source tree (`--source`) |\n\nModules then operate off that discovered map — so a check written once (\"secured\nroutes must reject anonymous callers\") runs against any FastAPI app\nunchanged. Auth-shape detection means the CSRF and session modules light up on a\ncookie-session app and stay quiet on a token API, with no per-app configuration.\n\n## Install\n\n```bash\nuv pip install -e '.[full]'      # full = JWT forging + pip-audit + TOML on 3.10\n# or minimal: uv pip install -e .\n```\n\n## Use\n\n### CLI\n\n```bash\n# Black-box, discovery only (no attacks) — a safe first look:\nheimdall --url http://127.0.0.1:8000 --discover-only\n\n# Full assessment, white-box (source tree enables JWT-secret + component checks):\nheimdall --url http://127.0.0.1:8000 \\\n         --source /path/to/YourApp \\\n         --cred admin:admin:admin@you.io:hunter2 \\\n         --cred member:user:member@you.io:pw\n\n# Or from a config file:\nheimdall --config examples/example.toml\n\n# Non-destructive pass, or scope to specific modules:\nheimdall --url http://127.0.0.1:8000 --safe\nheimdall --url http://127.0.0.1:8000 --only a01,a02\nheimdall --url http://127.0.0.1:8000 --skip race,cmdi,xxe\nheimdall --list-modules\n```\n\n#### Full flag reference\n\n| Flag | Purpose |\n|---|---|\n| `--url URL` | Target base URL (required unless `--config` supplies it). |\n| `--source PATH` | Target source tree — enables white-box checks (JWT-secret recovery, `a06` components, `sast`). |\n| `--name NAME` | Friendly app name for the report. |\n| `--config FILE` | TOML/JSON target config (CLI flags override its fields). |\n| `--cred label:role:identifier:password` | A login credential; repeatable. `role` is free-form (`admin`, `user`, …) and drives BFLA/BOLA pairing. |\n| `--launch CMD` / `--launch-cwd DIR` | Boot the target with a shell command first (and its working dir), then tear it down after. The target's own stdout/stderr is captured to a temp log and, on a failed boot, its tail is printed. |\n| `--launch-timeout SECONDS` | How long to wait for a `--launch` target to answer (default 180). Raise it for apps that run migrations / seed fixtures on first boot. |\n| `--spawn-db` / `--spawn-db-env VAR` | Spin up a throwaway DB matching the target's engine (Docker Postgres/MySQL/Mongo, or a SQLite file) and hand it to the target via `VAR` (default `SQLITE_DB`); torn down after. |\n| `--spawn-db-kind KIND` | Force the throwaway engine (`sqlite`/`postgres`/`mysql`/`mongo`) instead of auto-detecting from the source. `sqlite` is the dependency-free choice for any app with a SQLite mode — no Docker, no port wiring. |\n| `--db-url URL` | An existing throwaway SQLAlchemy URL to provision into (use `postgresql+psycopg2://…`). |\n| `--provision N` / `--provision-admins N` | Insert N low-privilege (and N admin) test users straight into the DB, so BOLA/IDOR/BFLA checks have real cross-tenant subjects even when self-registration is closed. |\n| `--no-mint` | Don't mint API-scoped JWTs even if the signing secret is recovered. |\n| `--no-attacker` | Don't self-register a throwaway low-priv attacker account. |\n| `--safe` | Skip every mutating/destructive probe. |\n| `--only KEYS` / `--skip KEYS` | Comma-separated module keys to include / exclude. |\n| `--fail-on LEVEL` | CI gate severity (`none,info,low,medium,high,critical`; default `high`). |\n| `--baseline findings.json` | Suppress already-known findings; gate only on new ones. |\n| `--discover-only` | Print the discovered profile (routes, auth, secrets) and exit — no attacks. |\n| `--list-modules` | List registered modules and exit. |\n| `--i-have-authorization` | Permit a non-loopback target (authorized use only). |\n| `--out DIR` / `--no-color` | Report output dir (default `./heimdall-report`) / disable ANSI colour. |\n\n#### Provisioning \u0026 throwaway targets\n\nFor a deep run against an app whose sign-up is closed (or to drive access-control\nchecks across *many* distinct owners), let Heimdall build the world:\n\n```bash\n# Boot the app onto a throwaway DB, seed real principals, then attack — all disposable:\nheimdall --url http://127.0.0.1:8100 --source /path/to/App \\\n         --launch '/path/to/App/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8100' \\\n         --launch-cwd /path/to/App \\\n         --spawn-db \\\n         --provision 3 --provision-admins 1 \\\n         --launch-timeout 300\n```\n\nPoint `--launch` at the target's *own* interpreter (e.g. its `.venv/bin/uvicorn`)\nso the boot command resolves the app's dependencies, and give `--launch-timeout`\nenough room for an app that migrates/seeds on first boot.\n\n`--provision` inserts distinct users (hashing passwords the way the app's own\nmodel expects) and logs each in through the real login flow, giving the `a01`\nBOLA/IDOR/BFLA probes genuine cross-user subjects. Combined with `--spawn-db` /\n`--launch`, a whole assessment — server, database, users — is created and torn\ndown around a single command.\n\nIf the app has a built-in **SQLite mode**, add `--spawn-db-kind sqlite` — the\ndependency-free throwaway (no Docker, no port wiring). Otherwise `--spawn-db`\nauto-detects the engine.\n\nPrefer a **server engine** (the app's real backend)? Use `--spawn-db-kind postgres`\n(or `mysql`) instead — Heimdall spins a throwaway Docker server, reads the DB\nconnection variables the app actually uses from its source (env vars *and*\npydantic-settings / Django-style fields), and points the app at the throwaway\n(folding the mapped port into the host when the app has no separate port var). If\nit can't find a single connection variable to override, it **refuses** rather than\nlet the app boot against its real database — so a throwaway run never touches\nproduction data.\n\n### Library\n\n```python\nimport heimdall\n\nresult = heimdall.assess(\n    base_url=\"http://127.0.0.1:8000\",\n    source_path=\"/path/to/YourApp\",              # optional white-box\n    credentials=[(\"admin\", \"admin\", \"admin@you.io\", \"hunter2\")],  # label,role,id,pw\n    safe=False,                                  # True → skip destructive probes\n    only={\"a01\", \"a02\"},                         # or skip={\"race\", \"cmdi\"}\n)\nprint(result.counts())            # {'CRITICAL': 1, 'HIGH': 3, 'SAFE': 9, ...}\nprint(result.report_paths)        # (findings.json, REPORT.md, REPORT.html, findings.sarif)\n```\n\nDrive discovery alone:\n\n```python\nprofile = heimdall.discover(\"http://127.0.0.1:8000\", source_path=\"…\")\nprint(heimdall.summarize(profile))\n```\n\n## Modules\n\n26 detectors, grouped by OWASP category. Run `heimdall --list-modules` for the\nlive registry. Modules marked **⚡** are *destructive* (they mutate state) and are\nskipped under `--safe`.\n\n| Key | Title | What it does |\n|---|---|---|\n| **A01 — Broken Access Control** | | |\n| `a01` | Access Control / IDOR | Unauth access to secured routes; BFLA on admin routes; BOLA/IDOR on object-by-id routes across owners |\n| `csrf` | Cross-Site Request Forgery | On cookie-session apps: session-cookie `SameSite` + anti-CSRF-token enforcement (auto-skipped for token auth) |\n| `data-exposure` | Excessive Data Exposure | Secrets/PII/other users' tokens in responses (own-token \u0026 public-metadata aware) |\n| `mass-assignment` ⚡ | Mass Assignment | Over-posting privileged fields (`is_admin`, `role`) on create/update, with a determinism gate |\n| `oidc` | OAuth / OIDC Flow | `state`/PKCE/`redirect_uri` handling on discovered OAuth flows |\n| `open-redirect` | Open Redirect | Redirect params that navigate off-host (parsed like a browser, not by substring) |\n| **A02 — Cryptographic Failures** | | |\n| `a02` | JWT forging | `alg:none` forgery + weak-HS256-secret crack, *proven by live acceptance*; token-in-query leak |\n| **A03 — Injection** | | |\n| `a03` | Injection | SQLi (error/boolean, reproducibility-gated) + reflected-XSS + SSTI + traversal + operator injection |\n| `sqli-blind` ⚡ | Blind SQLi | Time-based blind SQL injection |\n| `cmdi` ⚡ | OS Command Injection | Time-based OS command injection |\n| `xxe` ⚡ | XML External Entity | XXE file disclosure (structural passwd-line match) on XML-accepting routes |\n| `sast` | SAST → DAST chaining | Source-sink scan whose findings are re-probed live against the running app |\n| **A04 — Insecure Design** | | |\n| `business-logic` ⚡ | Numeric Abuse | Negative/overflow quantities \u0026 price/amount tampering |\n| `race` ⚡ | Race / TOCTOU | Concurrent double-spend; flags only *divergent* (cumulative) races, not idempotent ones |\n| `workflow` ⚡ | Multi-step Replay | Replaying a step for a double effect |\n| `resource-consumption` | Resource Consumption | Unbounded pagination / payload size / missing rate limits |\n| **A05 — Security Misconfiguration** | | |\n| `a05` | Misconfiguration | CORS reflection, missing security headers, docs exposure, stack-trace/debug leaks |\n| `host-header` | Host Header Attacks | Host/`X-Forwarded-Host` poisoning reflected into URLs |\n| `improper-inventory` | Sensitive Paths | Exposed `.env`/config/backup/VCS \u0026 undocumented admin surfaces |\n| `graphql` | GraphQL Exposure | Introspection, suggestions, unauth queries |\n| `websocket` | WebSocket Security | Origin/auth enforcement on discovered WS endpoints |\n| `file-upload` ⚡ | Unrestricted Upload | Dangerous file types accepted on upload sinks |\n| **A06 — Vulnerable Components** | | |\n| `a06` | Outdated Components | `pip-audit` + heuristic version checks (needs `--source`) |\n| **A07 — Authentication Failures** | | |\n| `a07` | Auth Failures | Login rate-limit + XFF bypass, user enumeration, weak passwords, lockout (429/423) vs OAuth-token endpoints |\n| `session` | Session Lifecycle | Token/session expiry, logout invalidation, rotation |\n| **A10 — SSRF** | | |\n| `a10` | SSRF | URL-accepting sink discovery + internal-target / cloud-metadata probes |\n\nEach module also emits **TESTED-SAFE** findings so the report distinguishes\n*verified-safe* from *untested*.\n\n## Output\n\nEach run writes to `heimdall-report/`:\n- `REPORT.md` — executive summary, **attack chains**, per-finding OWASP mapping,\n  indicative CVSS, evidence/PoC, reproduction, references, tools.\n- `REPORT.html` — the same, standalone + styled (shareable).\n- `findings.json` — machine-readable.\n- `findings.sarif` — SARIF 2.1.0 for GitHub/GitLab/Azure code-scanning\n  (real findings as `kind:fail`, TESTED-SAFE as `kind:pass`).\n\n## CI integration\n\n```bash\n# fail the build on any finding at/above a threshold (default: high)\nheimdall --url http://127.0.0.1:8000 --source . --fail-on high\n\n# regression gate: only fail on findings NOT already in a baseline report\nheimdall --url http://127.0.0.1:8000 --baseline known-findings.json --fail-on medium\n```\n\n`--fail-on {none,info,low,medium,high,critical}` sets the exit-code gate;\n`--baseline \u003cfindings.json\u003e` suppresses already-known/accepted findings so CI\nonly breaks on *new* ones. Upload `findings.sarif` with\n`github/codeql-action/upload-sarif` to surface results in the Security tab.\n\n## Try it — bundled vulnerable demo\n\nThe repo ships a deliberately-insecure FastAPI app so you can see Heimdall work\nend-to-end without pointing it at anything of your own:\n\n```bash\npip install -e '.[demo]'\nuvicorn examples.vulnerable_app.main:app --host 127.0.0.1 --port 8099\nheimdall --url http://127.0.0.1:8099 --source examples/vulnerable_app \\\n         --cred admin:admin:admin:admin123 --cred alice:user:alice:alice123\n```\n\nA clean run reports **~1 CRITICAL + 7-8 HIGH** — JWT-secret forgery, BOLA/IDOR,\nBFLA, reflected XSS, SSRF, credentialed CORS, PII/secret exposure, and more —\nalongside TESTED-SAFE verdicts on the app's few correct endpoints. Every finding\nmaps to a planted flaw documented in\n[`examples/vulnerable_app/README.md`](examples/vulnerable_app/README.md).\n\n## Safety \u0026 scope\n\n- **Guardrail:** Heimdall refuses any non-loopback target unless you pass\n  `--i-have-authorization`.\n- `--safe` skips every mutating/destructive probe.\n- Reports may contain secrets and target internals — the repo `.gitignore`\n  excludes them; keep them out of version control.\n\n### ⚖️ Authorized use only\n\nThis is a security-testing tool. **Use it only against systems you own or for\nwhich you have prior, explicit, written authorization to test.** Unauthorized\nscanning or exploitation of systems you do not own is illegal in most\njurisdictions (e.g. the US Computer Fraud and Abuse Act, EU Directive 2013/40/EU,\nthe UK Computer Misuse Act 1990, and equivalents). By using this software you\nconfirm you have the necessary authorization for every target and accept sole\nresponsibility for your use of it; the authors accept no liability for misuse.\nSee the [LICENSE](LICENSE) for the full disclaimer.\n\n## How it fits together\n\n```\nheimdall/\n  discovery/   openapi → RouteMap · auth detection (token/cookie) · JWKS · source secret scan → AppProfile\n  bootstrap/   launch target · spawn throwaway DB · provision users · mint principals via the real login flow\n  modules/     26 detectors, each reading the AppProfile — no hard-coded paths\n  core/        Context, Finding, report renderer (MD/HTML/SARIF), attack-chain builder, loopback guardrail\n  runner.py    launch → discover → bootstrap → modules → report\n```\n\n## Status\n\nAlpha. The discovery layer targets FastAPI/OpenAPI 3.x; the exploit modules are\nblack-box-first and conservative about false positives (unconfirmed SSRF sinks,\nheuristic enumeration, etc. are labelled as needing manual/OAST confirmation).\nA 60+-case test suite (`pytest`) pins each module's precision — every\nfalse-positive fix ships with a false-negative control and a regression test.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaximeroucher%2Fheimdall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaximeroucher%2Fheimdall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaximeroucher%2Fheimdall/lists"}