ssf is an enterprise-grade, asynchronous security auditing framework for Supabase projects. It goes beyond simple configuration checks to actively test for vulnerabilities like SQL Injection, IDOR, and Information Leakage.
Warning
The official website of ssf has not been updated yet. Please wait a moment.
- π‘οΈ Active Verification: Don't just guess; verify.
ssfattempts safe exploits (e.g., time-based SQLi) to confirm risks. - π€ AI-Powered Context: Integrates with Google Gemini to understand your specific schema and business logic for deeper insights.
- βοΈ CI/CD Ready: JSON output and diffing capabilities (
--diff) make it perfect for automated security pipelines. - π§ Smart Fuzzing: Uses context-aware payloads to detect hidden data leaks in RPCs.
| Feature | Description |
|---|---|
| RLS Analysis | Detects tables with missing or permissive Row Level Security policies. |
| Auth Leaks | Identifies public tables exposing user data (PII). |
| RPC Security | Enumerates and fuzzes executable Remote Procedure Calls for SQLi and leaks. |
| Storage Buckets | Checks for public write access and listing capabilities. |
| Realtime Channels | Detects open WebSocket channels and sniffs for sensitive events (--sniff). |
| PostgREST Config | Checks for dangerous configuration like unlimited max_rows (--check-config). |
| Edge Functions | Enumerates public Edge Functions. |
| Database Extensions | Detects 30+ extensions (e.g., pg_cron, pg_net) and assesses security risks. |
| GraphQL | Checks for introspection leaks, Query Depth, and Field Fuzzing. |
| JWT Attacks | Checks for weak secrets (none alg, weak keys) and token tampering. |
| OpenAPI Analysis | Parses swagger.json to find hidden endpoints and parameter schemas. |
| Stealth Mode | Spoofs JA3/TLS fingerprints to bypass WAFs using curl_cffi. |
| PostgREST Fuzzer | Advanced fuzzing of filter operators (eq, in, ov, sl) for SQLi/Bypass. |
| GraphQL Batching | Detects batching support and calculates max batch size to prevent DoS. |
| SARIF Output | Generates standard SARIF reports for GitHub Advanced Security integration. |
You can install ssf directly from PyPI using pip:
pip3 install supabase-audit-framework --upgradeYou can also run ssf using Docker. This ensures a consistent environment and avoids dependency conflicts.
-
Build the Docker image:
docker build -t ssf . -
Run the container:
docker run ssf --help
-
Running in Interactive Mode (Wizard): If you use the
--wizardmode or any feature requiring user input, you must use the-itflags to allocate a pseudo-TTY and keep stdin open:docker run -it ssf --wizard
-
Clone the repository:
git clone https://github.com/ThemeHackers/ssf cd ssf -
Install locally:
python3 -m venv .venv source .venv/bin/activate pip3 install -e .
Once installed, you can use the ssf command globally.
ssf <SUPABASE_URL> <ANON_KEY>Enable AI analysis, brute-forcing, and HTML reporting:
# Using Gemini (Cloud) ssf <URL> <KEY> --agent-provider gemini --agent gemini-2.0-flash --agent-key "YOUR_API_KEY" --brute --html --json # Using OpenAI (GPT-4) ssf <URL> <KEY> --agent-provider openai --agent gpt-4-turbo --agent-key "sk-..." --brute --html --json # Using Anthropic (Claude) ssf <URL> <KEY> --agent-provider anthropic --agent claude-3-5-sonnet-20240620 --agent-key "sk-ant-..." --brute --html --json # Using DeepSeek (DeepSeek-V3) ssf <URL> <KEY> --agent-provider deepseek --agent deepseek-chat --agent-key "sk-..." --brute --html --json # Using Ollama (Local) ssf <URL> <KEY> --agent-provider ollama --agent llama3 --brute --html --jsonTip
You can set environment variables instead of passing --agent-key:
- Gemini:
GEMINI_API_KEY - OpenAI:
OPENAI_API_KEY - Anthropic:
ANTHROPIC_API_KEY - DeepSeek:
DEEPSEEK_API_KEY
Block regressions by comparing against a baseline:
# 1. Generate baseline ssf <URL> <KEY> --json > baseline.json # 2. Compare in CI ssf <URL> <KEY> --json --diff baseline.jsonAutomatically run security audits on every push and pull request using GitHub Actions.
Create .github/workflows/supabase-security.yml:
name: Supabase Security Audit on: push: branches: [ "main" ] pull_request: branches: [ "main" ] schedule: - cron: '0 0 * * 0' jobs: security-audit: name: SSF Scan & Report runs-on: ubuntu-latest permissions: contents: read security-events: write steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install SSF Framework run: pip3 install supabase-audit-framework --upgrade - name: Run Security Scan continue-on-error: true env: SUPABASE_URL: ${{ secrets.SUPABASE_URL }} SUPABASE_KEY: ${{ secrets.SUPABASE_ANON_KEY }} run: | if [[ -z "$SUPABASE_URL" ]]; then echo "::error::Secret SUPABASE_URL is empty! Check Environment settings." exit 1 fi set +e ssf $SUPABASE_URL $SUPABASE_KEY \ --ci \ --ci-format github \ --fail-on HIGH \ --sarif \ --json EXIT_CODE=$? mv audit_report_*/*.sarif results.sarif mv audit_report_*/*.json scan_results.json exit $EXIT_CODE - name: Upload SARIF to GitHub Security if: always() uses: github/codeql-action/upload-sarif@v4 with: sarif_file: results.sarif category: ssf-audit - name: Upload Raw JSON Report if: always() uses: actions/upload-artifact@v4 with: name: ssf-full-report path: scan_results.json retention-days: 14Setup Instructions:
- Go to Settings > Secrets and variables > Actions in your repository.
- Add
SUPABASE_URLandSUPABASE_ANON_KEY. - The workflow will now run on every commit and upload results to Security > Code Scanning Alerts.
Scan your local source code for Supabase-specific vulnerabilities (e.g., hardcoded keys, weak RLS definitions in migrations):
ssf <URL> <KEY> --agent-provider gemini --agent-key "KEY" --analyze ./supabase/migrationsGenerate a SQL script to fix identified vulnerabilities:
ssf <URL> <KEY> --agent-provider gemini --agent-key "KEY" --gen-fixesTest for vertical escalation by providing multiple role tokens:
# roles.json: {"user1": "eyJ...", "admin": "eyJ..."} ssf <URL> <KEY> --roles roles.jsonGenerate a comprehensive threat model (DFD, Attack Paths) using AI:
ssf <URL> <KEY> --agent-provider gemini --agent-key "KEY" --threat-modelssf includes a modern, dark-themed Web Dashboard for managing scans, viewing reports, and analyzing risks.
- Dashboard: Real-time scan progress, live logs, and finding summaries.
- Scan History: View, download, and compare past scan reports.
- Risk Management: Review findings and "Accept Risks" with justifications.
- Exploit Manager: View and run generated exploits directly from the browser.
ssf <URL> <KEY> --webui # Optional: Specify port ssf <URL> <KEY> --webui --port 9090 # Optional: For Collaboration ssf <URL> <KEY> --webui --ngrok --auth username:passwordCreate a knowledge.json file to ignore known safe patterns:
{ "accepted_risks": [ { "pattern": "public_stats", "type": "rls", "reason": "Intentionally public dashboard data" } ] }Verify if accepted risks have been remediated and update the knowledge base:
ssf <URL> <KEY> --knowledge knowledge.json --verify-fix[*] Testing RPC: get_user_data [!] DATA LEAK via RPC 'get_user_data' β 5 rows [!] Potential SQL Injection in get_user_data (param: id) - Verifying... [!!!] CONFIRMED Time-Based SQL Injection in get_user_data (5.02s) Warning
Active Testing Warning: This tool performs active exploitation verification (e.g., SQL Injection fuzzing, RPC execution).
- Authorized Use Only: You must have explicit permission to scan the target.
- Data Privacy: Using
--agentsends scan summaries to Google Gemini.
π Read our full Security Policy before use.
Run scans using local LLMs (e.g., Llama 3) for privacy and offline usage:
# Ensure Ollama is running (ollama serve) ssf <URL> <KEY> --agent-provider ollama --agent llama3Use built-in tamper scripts or custom ones to bypass WAFs:
# Use built-in tamper ssf <URL> <KEY> --tamper randomcase # Available built-ins: # - randomcase: SeLECt * fRoM... # - charencode: URL encode # - doubleencode: Double URL encode # - unionall: UNION SELECT -> UNION ALL SELECT # - space2plus: space -> + # - version_comment: space -> /*!50000*/Tip
You can see the principle of creating plugins from files.:
plugins/dummy_plugin.pyTo ensure the ssf framework automatically discovers and executes your plugin, you must adhere to three specific technical rules:
-
Inheritance Principle Your plugin class must inherit from
BaseScanner(located incore.base).- Why: The system instantiates plugins by passing
client,verbose, andcontextarguments. TheBaseScannerclass handles this initialization, giving you access toself.client(for HTTP requests) andself.context(for shared data).
- Why: The system instantiates plugins by passing
-
Naming Convention Principle The name of your Class must end with the suffix
Scanner.- Why: The
PluginManageriterates through files in theplugins/directory and specifically filters for classes whereattr_name.endswith("Scanner"). If your class is namedMyPluginorSecurityCheck, it will be ignored.
- Why: The
-
Method Implementation Principle You must implement an asynchronous method named
scan.- Why: The
ScannerManagerexecutes plugins concurrently usingasyncio. It expects every loaded plugin instance to have anasync def scan(self)method that returns a Dictionary (which is then added to the final report).
- Why: The
To create a new plugin, simply create a new .py file (e.g., custom_check.py) inside the plugins/ directory with the following structure:
from typing import Dict, Any # 1. Import the base class from core.base import BaseScanner # 2. Define your class ending with 'Scanner' class CustomVulnerabilityScanner(BaseScanner): """ Documentation for your custom scanner. """ # 3. Implement the async scan method async def scan(self) -> Dict[str, Any]: self.log_info("[*] Starting Custom Vulnerability Scan...") results = { "found_issues": [], "risk": "SAFE" } try: # Use self.client to make HTTP requests # self.context contains data from previous scans (like 'users', 'tables') target_url = "/some/vulnerable/endpoint" response = await self.client.get(target_url) if response.status_code == 200: self.log_risk(f"Found issue at {target_url}", "HIGH") results["found_issues"].append(target_url) results["risk"] = "HIGH" except Exception as e: self.log_error(f"Custom scan failed: {e}") # Return a dictionary to be included in the final report return resultsOnce you save this file in the plugins/ folder, ssf will:
- Detect the file during startup.
- Import the module and find the
CustomVulnerabilityScannerclass. - Execute your
scan()method automatically alongside the built-in scanners.
| Argument | Description |
|---|---|
url | Target Supabase Project URL |
key | Public Anon Key |
--agent-provider <NAME> | AI Provider: gemini (default), ollama, openai, deepseek, anthropic |
--agent <MODEL> | AI Model Name (e.g., gemini-3-pro-preview, llama3, gpt-4) |
--agent-key <KEY> | AI API Key (for Gemini/OpenAI/DeepSeek/Anthropic) |
--brute | Enable dictionary attack for hidden tables |
--html | Generate a styled HTML report |
--json | Save raw results to JSON |
--diff <FILE> | Compare current scan vs previous JSON report |
--knowledge <FILE> | Path to accepted risks JSON file |
--ci | Exit with non-zero code on critical issues (for CI/CD) |
--fail-on <LEVEL> | Risk level to fail on (default: HIGH) |
--ci-format <FMT> | CI Output format (text/github) |
--proxy <URL> | Route traffic through an HTTP proxy |
--stealth | NEW: Enable Stealth Mode (JA3 Spoofing) |
--sarif | NEW: Generate SARIF report |
--exploit | DANGER: Auto-run generated exploits |
--gen-fixes | Generate SQL fix script from AI analysis |
--analyze <PATH> | Perform static analysis on local code files |
--edge_rpc <FILE> | Custom wordlist for Edge Functions |
--roles <FILE> | JSON file with role tokens for vertical escalation testing |
--threat-model | Generate Automated Threat Model (requires --agent) |
--verify-fix | Verify remediation of accepted risks |
--compile | Compile tool to standalone executable |
--verbose | Enable debug logging |
--dump-all | Dump all data from the database |
--sniff [SEC] | Enable Realtime Sniffer for N seconds (default: 10) |
--check-config | Check PostgREST configuration (max_rows) |
--wizard | Run in wizard mode for beginners |
--random-agent | Use a random User-Agent header |
--level <LEVEL> | Level of tests to perform (1-5, default 1) |
--tamper <NAME> | Tamper script name (built-in) or path to file |
--webui | Launch the Web Management Dashboard |
--port <PORT> | Port for Web UI (default: 8080) |
--ngrok | Expose Web UI via ngrok |
--auth <CREDENTIALS> | Username:Password for Web UI (e.g., admin:secret) |
--plugins <LIST> | Select plugins to run (comma-separated names or 'all') |
The developers assume no liability and are not responsible for any misuse or damage caused by this program. Use responsibly.
- See Project Github
- Python Package Index
- Quick Reference Gist - Shareable quick-start reference for SSF