Skip to main content

Safe-by-default impact-assessment tool for a leaked Supabase credential.

Project description

Supabase Credential Impact Assessor

A safe-by-default, read-only command-line tool for inventorying a leaked Supabase credential and assessing its blast radius during an authorized security assessment.

⚠️ Authorized use only. This tool is intended for internal product security teams triaging a credential leak they are authorized to assess. It defaults to non-destructive, read-only checks. Any write/delete or account creation behaviour is disabled unless you explicitly opt in.

The goal is impact assessment, not exploitation: quickly determine what a leaked key can reach so the owning product team can rotate it and remediate.


Features

Nine phases, mapped to the assessment workflow:

Phase Name What it does Network
1 Credential Identification Decode the JWT (header, payload, role, issuer, audience, expiry, project ref, custom claims); classify key kind none
2 Project Discovery Fingerprint project URL, ref, region hint, API version, server headers read-only
3 Permission Enumeration Reachability of REST, Storage, Edge Functions, Auth, Realtime read-only
4 Database Enumeration List tables/views/RPCs via PostgREST OpenAPI; SELECT … LIMIT 1 read-only*
5 Storage Enumeration List buckets, classify public/private, list objects read-only*
6 Auth Enumeration Signup/magic-link/reset/admin exposure via GoTrue settings read-only*
7 Privilege Matrix Per-resource SELECT/INSERT/UPDATE/DELETE/EXEC/Storage/Realtime grid
8 Evidence Collection Write report.md, report.html, raw.json, summary.json, logs/, screenshots/
9 Attribution Assistance Collect non-sensitive identifiers to help owners trace the key

* Optional intrusive validation is off by default — see below.

Safety model

  • Default: only read-only and metadata probes run. SELECT fetches at most one row and records column names only, never values.
  • --write-probes: enables non-destructive write validation:
    • INSERT → POST [] (empty array, inserts zero rows)
    • UPDATE → PATCH with an impossible filter (matches zero rows)
    • DELETE → DELETE with an impossible filter (matches zero rows)
    • Storage → uploads one uniquely-named temp object and immediately deletes it; RPCs are probed with an invalid argument signature so the function body never executes.
  • --active: enables auth actions that can create a real user (signup). Off by default; use only with explicit authorization.
  • The raw key is never written to disk — only a truncated SHA-256 fingerprint is stored in the evidence.
  • Every request is logged to logs/assessment.log and the console.

Install

cd tools/supabase-cred-assessment
python3.12 -m venv .venv && source .venv/bin/activate
pip install -e .
# or, without installing:
pip install -r requirements.txt

Usage

# Safe, read-only assessment (recommended default). Key is prompted securely.
supabase-assess --url https://YOURREF.supabase.co

# Provide the key non-interactively and choose an output directory.
supabase-assess -u https://YOURREF.supabase.co -k "<leaked-key>" -o ./out

# Opt into non-destructive write/delete validation.
supabase-assess -u https://YOURREF.supabase.co --write-probes

# Opt into auth actions that can create a real user (authorized only).
supabase-assess -u https://YOURREF.supabase.co --write-probes --active

# Run as a module:
python -m supabase_assessor --url https://YOURREF.supabase.co

Output

Written to the output directory (default ./assessments/supabase-<timestamp>/):

report.md         Human-readable Markdown report
report.html       Self-contained styled HTML report
raw.json          Complete assessment: every probe and result
summary.json      Compact machine-readable summary
logs/             assessment.log — full request audit trail
screenshots/      Placeholder for manual evidence

Each report contains: Executive Summary, Technical Findings, Risk Rating, Affected Resources (privilege matrix), Credential Details, Evidence (probe log), Attribution Assistance, and Recommended Mitigation.

Web UI (no install, browser-only)

A static, dependency-free web edition lives at web/index.html. Open it directly in a browser (or serve the folder) — there is no build step and no backend:

# Just open it:
xdg-open web/index.html        # or: open web/index.html  (macOS)

# ...or serve the folder locally:
python -m http.server -d web 8000   # then visit http://localhost:8000

Paste the Supabase URL and key and click Run assessment. The JWT is decoded and all read-only phases run client-side; results, the privilege matrix, and downloadable raw.json / report.md / report.html are produced in the tab. The key stays in memory only and is never stored or transmitted anywhere except to the project you name.

Browser limitation — graceful degradation: some probes depend on the target returning permissive CORS headers. If the browser blocks a response, that probe is marked ⨯ browser/CORS blocked and, if everything is blocked, the UI shows the equivalent CLI command so you can run the full Python tool instead.

How the assessment works

  1. Decode, don't guess. The key is first decoded locally (Phase 1). Supabase legacy keys are JWTs, so the role/issuer/expiry/project-ref are read straight from the token with zero network traffic.
  2. Fingerprint the project (Phase 2) via the PostgREST root, which also yields the exposed schema and server metadata.
  3. Map the surface, safely. The PostgREST OpenAPI document (Phase 4) lists every exposed table/view/RPC without touching information_schema or any data. Each relation is then probed with a single SELECT … LIMIT 1 that records column names only.
  4. Classify every outcome through one shared mapper so SELECT, storage, and auth results all speak the same vocabulary — that consistency is what makes the Phase 7 privilege matrix trustworthy.
  5. Score and report. Findings and an overall risk rating are derived from the probe results and written to JSON/Markdown/HTML evidence (Phase 8), plus non-sensitive attribution identifiers (Phase 9) the owning team can trace.

Writes are never attempted unless you opt in, and even then only via provably zero-effect techniques (empty-array insert, impossible-filter update/delete, temp-object upload+delete, non-executing RPC signature probe).

Build a release artifact

The CLI is a standard PEP 517 project (setuptools backend). To build the sdist + wheel that you can attach to a GitHub Release:

cd tools/supabase-cred-assessment
python -m pip install --upgrade build
python -m build            # writes dist/*.tar.gz and dist/*.whl

Install the built wheel anywhere with:

pip install dist/supabase_credential_assessor-1.0.0-py3-none-any.whl

(dist/ is git-ignored so build outputs never land in the repo.)

Development

pip install -e ".[dev]"
pytest                         # offline unit tests (JWT decode + classifier)
ruff check supabase_assessor   # lint
black --check supabase_assessor  # formatting
mypy supabase_assessor         # type check (advisory)

Releasing

Releases are automated by GitHub Actions (.github/workflows/release.yml). Because this repository ships two independent packages, each is released with its own tag prefix — this package uses supabase-credential-assessor-vX.Y.Z.

  1. Update the version in supabase_assessor/__init__.py (__version__) and add a CHANGELOG.md entry.
  2. Commit the changes.
  3. Push a version tag:
    git tag supabase-credential-assessor-v1.2.3
    git push origin supabase-credential-assessor-v1.2.3
    
  4. GitHub Actions builds the sdist + wheel, runs twine check, generates SHA256SUMS, and creates a GitHub Release with auto-generated notes and the assets attached.
  5. A final version (X.Y.Z) is published to PyPI; a pre-release such as 1.2.3rc1 is published to TestPyPI. Both use OIDC Trusted Publishing (no API tokens).

Alternatively, run the Manual Release workflow from the Actions tab (workflow_dispatch) to bump the version, test, build, and release — with prerelease / draft / publish-to-index toggles.

One-time setup: on PyPI (and TestPyPI) add a Trusted Publisher for this package pointing at repo ajtazer/ajtazer, workflow release.yml, and environment pypi-supabase-credential-assessor (respectively testpypi-supabase-credential-assessor). Each package uses its own environment so the two configs stay distinct — PyPI rejects duplicate pending-publisher configs.

Architecture

supabase_assessor/
  models.py            Typed dataclasses (single source of truth)
  http_client.py       Async httpx client: logging + retry/backoff
  classify.py          Status-code -> AccessResult normalisation
  logging_config.py    Rich console + file logging
  phase1_jwt.py        Phase 1 (offline)
  phase2_discovery.py  Phase 2
  phase3_permissions.py Phase 3
  phase4_database.py   Phase 4
  phase5_storage.py    Phase 5
  phase6_auth.py       Phase 6
  phase7_matrix.py     Phase 7 (matrix rendering)
  phase9_attribution.py Phase 9
  analysis.py          Findings + overall risk rating
  reporting.py         Phase 8 (JSON/Markdown/HTML)
  orchestrator.py      Runs all phases with a progress bar
  cli.py               Typer CLI entry point
web/index.html         Static, browser-only edition (no build step)
tests/                 Offline unit tests
pyproject.toml         Packaging metadata (PEP 517, setuptools)

License

MIT. Provided for authorized defensive security assessment only.

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

supabase_credential_assessor-1.0.0.tar.gz (38.5 kB view details)

Uploaded Source

Built Distribution

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

supabase_credential_assessor-1.0.0-py3-none-any.whl (42.4 kB view details)

Uploaded Python 3

File details

Details for the file supabase_credential_assessor-1.0.0.tar.gz.

File metadata

File hashes

Hashes for supabase_credential_assessor-1.0.0.tar.gz
Algorithm Hash digest
SHA256 3ee9840ccde879da47c86f21fb7e75c9bc660647f7a5bf27a71b7c28723e76b5
MD5 8021979844ec08e1b5c2504008655c59
BLAKE2b-256 3809d82e85a8c0c5a112597d35777026dd2772d81157ee7cf099c3da007d43b9

See more details on using hashes here.

Provenance

The following attestation bundles were made for supabase_credential_assessor-1.0.0.tar.gz:

Publisher: release.yml on ajtazer/ajtazer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file supabase_credential_assessor-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for supabase_credential_assessor-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 36f947cddca64beceda01894f598df70cd3515683f0ddc817142c4de9cbbe073
MD5 237de6d846b2f570ab8f94f2da2f5571
BLAKE2b-256 fc6ea5e95ca16a2da5d4240ea6c779c5e08833c29f0472c087ed9edeb63a8a4b

See more details on using hashes here.

Provenance

The following attestation bundles were made for supabase_credential_assessor-1.0.0-py3-none-any.whl:

Publisher: release.yml on ajtazer/ajtazer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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