Skip to main content

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 via psql.
  • JWT bypass prober — enumerates Supabase Edge Functions via the Management API and probes each one with an unauthenticated POST {} to surface verify_jwt=false exposures 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+
  • psql on PATH for the pivot subcommand
  • A Postgres connection string with read access for pivot
  • A Supabase Management API token (SUPABASE_ACCESS_TOKEN) with read:functions for jwt-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 pivot subcommand only runs SELECT statements against system catalogs and auth.* tables.
  • The jwt-probe subcommand only sends POST {} 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 interactively
  • db-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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

db_fortress-0.1.0.tar.gz (12.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

db_fortress-0.1.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

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

Hashes for db_fortress-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a6db3bba123734dba4ed4f5ff9fe780ca5434814fc486f6a6844ac7b169a1663
MD5 b83ac4f63a03cd7e72672651c2b23123
BLAKE2b-256 892a47337383401205d878246ba9dcf2e97b91bdcdb4632738c92a8aaa52590d

See more details on using hashes here.

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

Hashes for db_fortress-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a8f947bc50d4f3a2f6d0444c89e1e5f602b35a9c14040af9a4da40894b058c5
MD5 d51cedba5e7ba6418dcd5203cd8aa340
BLAKE2b-256 51c37da7483ead44ee1734124416edc89205e98c7402d9888c62dd96f96b5c07

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page