PostgreSQL/Supabase database security scanner — supply-chain pivot detection + JWT bypass prober
Project description
db-fortress
PostgreSQL / Supabase database security scanner.
Two complementary probes for production posture:
- supply-chain pivot — detects suspicious session bursts, long-lived sessions, OAuth identity creation spikes, and unknown superuser roles in
pg_roles. Read-only viapsql. - JWT bypass prober — enumerates Supabase Edge Functions via the Management API and probes each one with an unauthenticated
POST {}to surfaceverify_jwt=falseexposures that are not on an explicit allowlist.
Both probes emit JSON reports with deterministic severity scoring (LOW / MEDIUM / HIGH / CRITICAL) and exit codes you can wire into CI.
Why
Most database scanners check CVEs and dependencies. db-fortress checks production posture of the running database — the configuration drift that vibe-coded apps and AI-generated migrations typically ship with: weak roles, exposed Edge Functions, abnormal session patterns. It is built to run as a scheduled job and produce a signed JSON artifact, not as an interactive UI.
Install
pip install db-fortress
Or from source:
git clone https://github.com/Omji-krypto/db-fortress
cd db-fortress
pip install -e .
Requirements
- Python 3.10+
psqlonPATHfor thepivotsubcommand- A Postgres connection string with read access for
pivot - A Supabase Management API token (
SUPABASE_ACCESS_TOKEN) withread:functionsforjwt-probe
Usage
Supply-chain pivot scan
export SUPABASE_DB_URL="postgresql://reader:***@db.example.supabase.co:5432/postgres"
db-fortress pivot \
--window-hours 72 \
--output supply-chain-pivot.json
Or pass the connection string explicitly:
db-fortress pivot --dburl "$SUPABASE_DB_URL" --output report.json
Exit codes: 0 LOW/MEDIUM, 1 HIGH, 2 CRITICAL.
JWT bypass probe
This subcommand sends real HTTP requests to your project's Edge Functions. By design it only sends POST {} with no Authorization header — it does not modify state — but it is active traffic against your project. It is gated behind --active.
export SUPABASE_ACCESS_TOKEN="sbp_..."
db-fortress jwt-probe \
--active \
--project-ref abcdwxyz1234 \
--baseline ./examples/baseline.example.json \
--output jwt-bypass-probe.json
The baseline file declares which Edge Functions are intentionally public:
{
"ef_verify_jwt_false_allowlist": [
"public-webhook-receiver",
"public-health-check"
]
}
Any function returning 200/201/202/204/400/422 to an unauthenticated POST {} and not in ef_verify_jwt_false_allowlist is reported as unauth_UNEXPECTED with score 150 (CRITICAL). Functions returning 401/403 are reported as protected.
Exit codes: 0 LOW/MEDIUM, 1 HIGH, 2 CRITICAL.
Without --active
If you call db-fortress jwt-probe without --active, the command refuses to run and prints the warning. This is intentional: you should not point an active scanner at a project unless you know what it does.
Pro tier · early access waitlist
db-fortress is the open core. Pro tier adds:
- 120+ controls (vs 30 in the free CLI)
- Continuous monitoring
- Bilingual reports (EN / ES / ZH)
- Aegis VC sign per finding
- Priority support
Join the waitlist · invites sent in signup order:
https://artefactforge.ai/db-fortress
CLI
db-fortress pivot [--dburl URL] [--window-hours N] [--output FILE]
db-fortress jwt-probe --active --project-ref REF [--baseline FILE] [--output FILE] [--max-functions N]
db-fortress --version
Output schema
pivot JSON:
{
"generated": "2026-05-09T10:00:00+00:00",
"window_hours": 72,
"total_score": 240,
"severity": "CRITICAL",
"findings": [
{"check": "session_burst", "rows_found": 4, "score": 120, "flags": ["session_burst_2026-05-09T03:00:00+00:00:62"]},
{"check": "long_sessions", "rows_found": 12, "score": 80, "flags": ["long_sessions_count:12"]},
{"check": "new_oauth_identities", "rows_found": 28, "score": 40, "flags": ["oauth_activity_google:8"]},
{"check": "admin_role_assignments", "rows_found": 9, "score": 0, "flags": []},
{"check": "table_activity", "rows_found": 50, "score": 0, "flags": [], "sample": [["public", "events", "1234", "0", "", ""]]}
]
}
jwt-probe JSON:
{
"generated": "2026-05-09T10:00:00+00:00",
"project_ref": "abcdwxyz1234",
"total_functions": 14,
"allowlist_size": 2,
"critical_count": 1,
"high_count": 0,
"findings": [
{"slug": "internal-admin-tool", "status": 200, "verdict": "unauth_UNEXPECTED", "score": 150, "flags": ["verify_jwt_false", "NOT_allowlisted"], "body_preview": "{\"ok\":true}"}
]
}
Severity scoring
| Score range | Severity |
|---|---|
| ≥ 151 | CRITICAL |
| 81 – 150 | HIGH |
| 31 – 80 | MEDIUM |
| 0 – 30 | LOW |
Safety
- The
pivotsubcommand only runsSELECTstatements against system catalogs andauth.*tables. - The
jwt-probesubcommand only sendsPOST {}payloads. It never sends a non-empty body and never authenticates. - Neither subcommand writes to your database.
- All output is local JSON — nothing is uploaded.
If you embed this in CI, set a low --max-functions and pin a baseline file in your repo so a forgotten public Edge Function fails the build.
Roadmap
db-fortress baseline— generate an initial baseline interactivelydb-fortress sign— Ed25519-signed JSON reports (drop-in for compliance attestations)- Postgres-only mode (drop the Supabase Management API dependency for plain Postgres deployments)
- Coverage of additional posture checks (RLS USING
true, BOLA detection on REST endpoints)
Contributing
Issues and PRs welcome. Please open an issue before sending large changes — this repo intentionally stays small.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file db_fortress-0.1.0.tar.gz.
File metadata
- Download URL: db_fortress-0.1.0.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6db3bba123734dba4ed4f5ff9fe780ca5434814fc486f6a6844ac7b169a1663
|
|
| MD5 |
b83ac4f63a03cd7e72672651c2b23123
|
|
| BLAKE2b-256 |
892a47337383401205d878246ba9dcf2e97b91bdcdb4632738c92a8aaa52590d
|
File details
Details for the file db_fortress-0.1.0-py3-none-any.whl.
File metadata
- Download URL: db_fortress-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a8f947bc50d4f3a2f6d0444c89e1e5f602b35a9c14040af9a4da40894b058c5
|
|
| MD5 |
d51cedba5e7ba6418dcd5203cd8aa340
|
|
| BLAKE2b-256 |
51c37da7483ead44ee1734124416edc89205e98c7402d9888c62dd96f96b5c07
|