supabase-audit-functions by yoanbernabeu/supabase-pentest-skills
npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-audit-functions🔴 关键:需要渐进式文件更新
你必须在测试过程中就写入上下文文件,而不是等到最后才写。
- 测试完每个函数后立即写入
.sb-pentest-context.json- 在每个函数测试前后记录到
.sb-pentest-audit.log- 不要等到技能完成才更新文件
- 如果技能崩溃或被中断,所有先前的发现必须已经保存
这不是可选的。未能渐进式写入是一个严重错误。
此技能用于发现并测试 Supabase Edge Functions 的安全问题。
Supabase Edge Functions 是基于 Deno 的无服务器函数:
https://[project].supabase.co/functions/v1/[function-name]
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 安全方面 | 注意事项 |
|---|---|
| 身份验证 | 函数可能需要 JWT 或为公开 |
| CORS | 跨域访问控制 |
| 输入验证 | 用户输入处理 |
| 密钥 | 环境变量暴露 |
| 测试 | 目的 |
|---|---|
| 函数发现 | 查找暴露的函数 |
| 身份验证要求 | 检查是否需要 JWT |
| 输入验证 | 测试注入漏洞 |
| 错误处理 | 检查信息泄露 |
Audit Edge Functions on my Supabase project
Test the process-payment Edge Function for security issues
═══════════════════════════════════════════════════════════
EDGE FUNCTIONS 审计
═══════════════════════════════════════════════════════════
项目: abc123def.supabase.co
端点: https://abc123def.supabase.co/functions/v1/
─────────────────────────────────────────────────────────
函数发现
─────────────────────────────────────────────────────────
发现方法: 常见名称枚举 + 客户端代码分析
发现的函数: 5
─────────────────────────────────────────────────────────
1. hello-world
─────────────────────────────────────────────────────────
端点: /functions/v1/hello-world
方法: GET, POST
身份验证测试:
├── 无 JWT: ✅ 200 OK
└── 状态: ℹ️ 公开函数(无需身份验证)
响应:
```json
{"message": "Hello, World!"}
评估: ✅ 合适 简单的公开端点,无敏感操作。
───────────────────────────────────────────────────────── 2. process-payment ─────────────────────────────────────────────────────────
端点: /functions/v1/process-payment 方法: POST
身份验证测试: ├── 无 JWT: ❌ 401 未授权 ├── 使用有效 JWT: ✅ 200 OK └── 状态: ✅ 需要身份验证
输入验证测试: ├── 缺少金额: ❌ 400 错误请求(良好) ├── 负金额: ❌ 400 错误请求(良好) ├── 字符串金额: ❌ 400 错误请求(良好) └── 有效输入: ✅ 200 OK
错误响应测试: ├── 错误格式: 通用消息(良好) └── 堆栈跟踪: ❌ 未暴露(良好)
评估: ✅ 安全得当 需要身份验证,验证输入,安全的错误处理。
───────────────────────────────────────────────────────── 3. get-user-data ─────────────────────────────────────────────────────────
端点: /functions/v1/get-user-data 方法: GET
身份验证测试: ├── 无 JWT: ❌ 401 未授权 └── 状态: ✅ 需要身份验证
授权测试: ├── 请求自己的数据: ✅ 200 OK ├── 请求其他用户的数据: ✅ 200 OK ← 🔴 P0! └── 状态: 🔴 访问控制失效
测试:
# 以用户 A 的身份,请求用户 B 的数据
curl https://abc123def.supabase.co/functions/v1/get-user-data?user_id=user-b-id \
-H "Authorization: Bearer [user-a-token]"
# 返回用户 B 的数据!
发现: 🔴 P0 - IDOR 漏洞 函数接受 user_id 参数,但未验证经过身份验证的用户是否在请求自己的数据。
修复:
// 在 Edge Function 中
const { user_id } = await req.json();
const jwt_user = getUser(req); // 从 JWT 获取
// 验证所有权
if (user_id !== jwt_user.id) {
return new Response('Forbidden', { status: 403 });
}
───────────────────────────────────────────────────────── 4. admin-panel ─────────────────────────────────────────────────────────
端点: /functions/v1/admin-panel 方法: GET, POST
身份验证测试: ├── 无 JWT: ❌ 401 未授权 ├── 使用普通用户 JWT: ✅ 200 OK ← 🔴 P0! └── 状态: 🔴 缺少角色检查
发现: 🔴 P0 - 权限提升 任何经过身份验证的用户都可以访问管理功能。函数代码中没有角色验证。
修复:
// 验证管理员角色
const user = getUser(req);
const { data: profile } = await supabase
.from('profiles')
.select('is_admin')
.eq('id', user.id)
.single();
if (!profile?.is_admin) {
return new Response('Forbidden', { status: 403 });
}
───────────────────────────────────────────────────────── 5. webhook-handler ─────────────────────────────────────────────────────────
端点: /functions/v1/webhook-handler 方法: POST
身份验证测试: ├── 无 JWT: ✅ 200 OK(Webhook 端点通常如此) └── 状态: ℹ️ 公开(Webhook 端点通常是公开的)
Webhook 安全测试: ├── 签名验证: ⚠️ 无法测试(需要有效签名) └── 速率限制: 未知
错误响应测试:
{
"error": "Invalid signature",
"expected": "sha256=abc123...",
"received": "sha256=xyz789..."
}
发现: 🟠 P1 - 信息泄露 错误响应揭示了预期的签名格式。可能帮助攻击者理解验证机制。
修复:
// 通用错误,在服务器端记录详细信息
if (!validSignature) {
console.error(`Invalid signature: expected ${expected}, got ${received}`);
return new Response('Unauthorized', { status: 401 });
}
───────────────────────────────────────────────────────── 总结 ─────────────────────────────────────────────────────────
发现的函数: 5
安全评估: ├── ✅ 安全: 2 (hello-world, process-payment) ├── 🔴 P0: 2 (get-user-data IDOR, admin-panel 权限提升) └── 🟠 P1: 1 (webhook-handler 信息泄露)
关键发现:
优先行动:
═══════════════════════════════════════════════════════════
## 常见函数漏洞
| 漏洞 | 描述 | 严重性 |
|---------------|-------------|----------|
| 无身份验证 | 无需 JWT 即可访问函数 | P0-P2 |
| IDOR | 用户可以访问其他用户的数据 | P0 |
| 缺少角色检查 | 普通用户访问管理员功能 | P0 |
| 输入注入 | 用户输入未经验证 | P0-P1 |
| 信息泄露 | 错误揭示内部细节 | P1-P2 |
| CORS 配置错误 | 可从非预期来源访问 | P1-P2 |
## 函数发现方法
### 1. 客户端代码分析
```javascript
// 在客户端代码中查找函数调用
supabase.functions.invoke('function-name', {...})
fetch('/functions/v1/function-name', {...})
测试的函数名称:
404 Not Found → 函数不存在
401 Unauthorized → 函数存在,需要身份验证
200 OK → 函数存在,可访问
{
"functions_audit": {
"timestamp": "2025-01-31T14:30:00Z",
"functions_found": 5,
"findings": [
{
"function": "get-user-data",
"severity": "P0",
"vulnerability": "IDOR",
"description": "Any authenticated user can access any user's data",
"remediation": "Verify user owns requested resource"
},
{
"function": "admin-panel",
"severity": "P0",
"vulnerability": "Privilege Escalation",
"description": "No role check, any authenticated user is admin",
"remediation": "Add admin role verification"
}
]
}
}
import { createClient } from '@supabase/supabase-js'
Deno.serve(async (req) => {
// 从头部获取 JWT
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response('Unauthorized', { status: 401 });
}
// 使用 Supabase 验证 JWT
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_ANON_KEY')!,
{ global: { headers: { Authorization: authHeader } } }
);
const { data: { user }, error } = await supabase.auth.getUser();
if (error || !user) {
return new Response('Unauthorized', { status: 401 });
}
// 用户已通过身份验证
// ...
});
// 针对用户特定资源
const requestedUserId = body.user_id;
const authenticatedUserId = user.id;
if (requestedUserId !== authenticatedUserId) {
return new Response('Forbidden', { status: 403 });
}
// 检查管理员角色
const { data: profile } = await supabase
.from('profiles')
.select('role')
.eq('id', user.id)
.single();
if (profile?.role !== 'admin') {
return new Response('Forbidden', { status: 403 });
}
import { z } from 'zod';
const PaymentSchema = z.object({
amount: z.number().positive().max(10000),
currency: z.enum(['usd', 'eur', 'gbp']),
description: z.string().max(200).optional()
});
// 验证输入
const result = PaymentSchema.safeParse(body);
if (!result.success) {
return new Response(
JSON.stringify({ error: 'Invalid input' }),
{ status: 400 }
);
}
⚠️ 此技能必须在执行过程中渐进式更新跟踪文件,而不是仅在最后更新。
不要在最后批量写入所有内容。而是:
.sb-pentest-audit.log.sb-pentest-context.json这确保了如果技能被中断、崩溃或超时,所有到该点为止的发现都能被保留。
更新.sb-pentest-context.json 并包含结果:
{ "functions_audit": { "timestamp": "...", "functions_found": 5, "findings": [ ... ] } }
记录到.sb-pentest-audit.log:
[TIMESTAMP] [supabase-audit-functions] [START] Auditing Edge Functions [TIMESTAMP] [supabase-audit-functions] [FINDING] P0: IDOR in get-user-data [TIMESTAMP] [supabase-audit-functions] [CONTEXT_UPDATED] .sb-pentest-context.json updated
如果文件不存在,在写入前创建它们。
未能更新上下文文件是不可接受的。
📁 证据目录: .sb-pentest-evidence/07-functions-audit/
| 文件 | 内容 |
|---|---|
discovered-functions.json | 发现的 Edge Functions 列表 |
function-tests/[name].json | 每个函数的测试结果 |
{
"evidence_id": "FN-001",
"timestamp": "2025-01-31T11:10:00Z",
"category": "functions-audit",
"type": "idor_vulnerability",
"severity": "P0",
"function": "get-user-data",
"endpoint": "https://abc123def.supabase.co/functions/v1/get-user-data",
"tests": [
{
"test_name": "auth_required",
"request": {
"method": "GET",
"headers": {},
"curl_command": "curl '$URL/functions/v1/get-user-data'"
},
"response": {"status": 401},
"result": "PASS"
},
{
"test_name": "idor_test",
"description": "As user A, request user B's data",
"request": {
"method": "GET",
"url": "$URL/functions/v1/get-user-data?user_id=user-b-id",
"headers": {"Authorization": "Bearer [USER_A_TOKEN]"},
"curl_command": "curl '$URL/functions/v1/get-user-data?user_id=user-b-id' -H 'Authorization: Bearer [USER_A_TOKEN]'"
},
"response": {
"status": 200,
"body": {"id": "user-b-id", "email": "[REDACTED]", "data": "[REDACTED]"}
},
"result": "VULNERABLE",
"impact": "Any authenticated user can access any other user's data"
}
],
"remediation": "Add ownership check: if (user_id !== jwt_user.id) return 403"
}
{
"evidence_id": "FN-002",
"timestamp": "2025-01-31T11:15:00Z",
"category": "functions-audit",
"type": "privilege_escalation",
"severity": "P0",
"function": "admin-panel",
"test": {
"description": "Regular user accessing admin function",
"request": {
"method": "GET",
"headers": {"Authorization": "Bearer [REGULAR_USER_TOKEN]"},
"curl_command": "curl '$URL/functions/v1/admin-panel' -H 'Authorization: Bearer [REGULAR_USER_TOKEN]'"
},
"response": {
"status": 200,
"body": {"admin_data": "[REDACTED]"}
},
"result": "VULNERABLE",
"impact": "Any authenticated user has admin access"
}
}
supabase-audit-rpc — 数据库函数(不同于 Edge Functions)supabase-audit-auth-config — 身份验证配置supabase-report — 包含在最终报告中每周安装次数
103
仓库
GitHub 星标数
33
首次出现
2026年1月31日
安全审计
安装于
claude-code85
codex79
opencode78
gemini-cli75
github-copilot70
cursor70
🔴 CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED
You MUST write to context files AS YOU GO , not just at the end.
- Write to
.sb-pentest-context.jsonIMMEDIATELY after each function tested- Log to
.sb-pentest-audit.logBEFORE and AFTER each function test- DO NOT wait until the skill completes to update files
- If the skill crashes or is interrupted, all prior findings must already be saved
This is not optional. Failure to write progressively is a critical error.
This skill discovers and tests Supabase Edge Functions for security issues.
Supabase Edge Functions are Deno-based serverless functions:
https://[project].supabase.co/functions/v1/[function-name]
| Security Aspect | Consideration |
|---|---|
| Authentication | Functions can require JWT or be public |
| CORS | Cross-origin access control |
| Input Validation | User input handling |
| Secrets | Environment variable exposure |
| Test | Purpose |
|---|---|
| Function discovery | Find exposed functions |
| Auth requirements | Check if JWT required |
| Input validation | Test for injection |
| Error handling | Check for information disclosure |
Audit Edge Functions on my Supabase project
Test the process-payment Edge Function for security issues
═══════════════════════════════════════════════════════════
EDGE FUNCTIONS AUDIT
═══════════════════════════════════════════════════════════
Project: abc123def.supabase.co
Endpoint: https://abc123def.supabase.co/functions/v1/
─────────────────────────────────────────────────────────
Function Discovery
─────────────────────────────────────────────────────────
Discovery Method: Common name enumeration + client code analysis
Functions Found: 5
─────────────────────────────────────────────────────────
1. hello-world
─────────────────────────────────────────────────────────
Endpoint: /functions/v1/hello-world
Method: GET, POST
Authentication Test:
├── Without JWT: ✅ 200 OK
└── Status: ℹ️ Public function (no auth required)
Response:
```json
{"message": "Hello, World!"}
Assessment: ✅ APPROPRIATE Simple public endpoint, no sensitive operations.
───────────────────────────────────────────────────────── 2. process-payment ─────────────────────────────────────────────────────────
Endpoint: /functions/v1/process-payment Method: POST
Authentication Test: ├── Without JWT: ❌ 401 Unauthorized ├── With valid JWT: ✅ 200 OK └── Status: ✅ Authentication required
Input Validation Test: ├── Missing amount: ❌ 400 Bad Request (good) ├── Negative amount: ❌ 400 Bad Request (good) ├── String amount: ❌ 400 Bad Request (good) └── Valid input: ✅ 200 OK
Error Response Test: ├── Error format: Generic message (good) └── Stack trace: ❌ Not exposed (good)
Assessment: ✅ PROPERLY SECURED Requires auth, validates input, safe error handling.
───────────────────────────────────────────────────────── 3. get-user-data ─────────────────────────────────────────────────────────
Endpoint: /functions/v1/get-user-data Method: GET
Authentication Test: ├── Without JWT: ❌ 401 Unauthorized └── Status: ✅ Authentication required
Authorization Test: ├── Request own data: ✅ 200 OK ├── Request other user's data: ✅ 200 OK ← 🔴 P0! └── Status: 🔴 BROKEN ACCESS CONTROL
Test:
# As user A, request user B's data
curl https://abc123def.supabase.co/functions/v1/get-user-data?user_id=user-b-id \
-H "Authorization: Bearer [user-a-token]"
# Returns user B's data!
Finding: 🔴 P0 - IDOR VULNERABILITY Function accepts user_id parameter without verifying that the authenticated user is requesting their own data.
Fix:
// In Edge Function
const { user_id } = await req.json();
const jwt_user = getUser(req); // From JWT
// Verify ownership
if (user_id !== jwt_user.id) {
return new Response('Forbidden', { status: 403 });
}
───────────────────────────────────────────────────────── 4. admin-panel ─────────────────────────────────────────────────────────
Endpoint: /functions/v1/admin-panel Method: GET, POST
Authentication Test: ├── Without JWT: ❌ 401 Unauthorized ├── With regular user JWT: ✅ 200 OK ← 🔴 P0! └── Status: 🔴 MISSING ROLE CHECK
Finding: 🔴 P0 - PRIVILEGE ESCALATION Admin function accessible to any authenticated user. No role verification in function code.
Fix:
// Verify admin role
const user = getUser(req);
const { data: profile } = await supabase
.from('profiles')
.select('is_admin')
.eq('id', user.id)
.single();
if (!profile?.is_admin) {
return new Response('Forbidden', { status: 403 });
}
───────────────────────────────────────────────────────── 5. webhook-handler ─────────────────────────────────────────────────────────
Endpoint: /functions/v1/webhook-handler Method: POST
Authentication Test: ├── Without JWT: ✅ 200 OK (expected for webhooks) └── Status: ℹ️ Public (webhook endpoints are typically public)
Webhook Security Test: ├── Signature validation: ⚠️ Unable to test (need valid signature) └── Rate limiting: Unknown
Error Response Test:
{
"error": "Invalid signature",
"expected": "sha256=abc123...",
"received": "sha256=xyz789..."
}
Finding: 🟠 P1 - INFORMATION DISCLOSURE Error response reveals expected signature format. Could help attacker understand validation mechanism.
Fix:
// Generic error, log details server-side
if (!validSignature) {
console.error(`Invalid signature: expected ${expected}, got ${received}`);
return new Response('Unauthorized', { status: 401 });
}
───────────────────────────────────────────────────────── Summary ─────────────────────────────────────────────────────────
Functions Found: 5
Security Assessment: ├── ✅ Secure: 2 (hello-world, process-payment) ├── 🔴 P0: 2 (get-user-data IDOR, admin-panel privilege escalation) └── 🟠 P1: 1 (webhook-handler info disclosure)
Critical Findings:
Priority Actions:
═══════════════════════════════════════════════════════════
## Common Function Vulnerabilities
| Vulnerability | Description | Severity |
|---------------|-------------|----------|
| No auth | Function accessible without JWT | P0-P2 |
| IDOR | User can access other users' data | P0 |
| Missing role check | Regular user accesses admin functions | P0 |
| Input injection | User input not validated | P0-P1 |
| Info disclosure | Errors reveal internal details | P1-P2 |
| CORS misconfigured | Accessible from unintended origins | P1-P2 |
## Function Discovery Methods
### 1. Client Code Analysis
```javascript
// Look for function invocations in client code
supabase.functions.invoke('function-name', {...})
fetch('/functions/v1/function-name', {...})
Tested function names:
404 Not Found → Function doesn't exist
401 Unauthorized → Function exists, needs auth
200 OK → Function exists, accessible
{
"functions_audit": {
"timestamp": "2025-01-31T14:30:00Z",
"functions_found": 5,
"findings": [
{
"function": "get-user-data",
"severity": "P0",
"vulnerability": "IDOR",
"description": "Any authenticated user can access any user's data",
"remediation": "Verify user owns requested resource"
},
{
"function": "admin-panel",
"severity": "P0",
"vulnerability": "Privilege Escalation",
"description": "No role check, any authenticated user is admin",
"remediation": "Add admin role verification"
}
]
}
}
import { createClient } from '@supabase/supabase-js'
Deno.serve(async (req) => {
// Get JWT from header
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
return new Response('Unauthorized', { status: 401 });
}
// Verify JWT with Supabase
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_ANON_KEY')!,
{ global: { headers: { Authorization: authHeader } } }
);
const { data: { user }, error } = await supabase.auth.getUser();
if (error || !user) {
return new Response('Unauthorized', { status: 401 });
}
// User is authenticated
// ...
});
// For user-specific resources
const requestedUserId = body.user_id;
const authenticatedUserId = user.id;
if (requestedUserId !== authenticatedUserId) {
return new Response('Forbidden', { status: 403 });
}
// Check admin role
const { data: profile } = await supabase
.from('profiles')
.select('role')
.eq('id', user.id)
.single();
if (profile?.role !== 'admin') {
return new Response('Forbidden', { status: 403 });
}
import { z } from 'zod';
const PaymentSchema = z.object({
amount: z.number().positive().max(10000),
currency: z.enum(['usd', 'eur', 'gbp']),
description: z.string().max(200).optional()
});
// Validate input
const result = PaymentSchema.safeParse(body);
if (!result.success) {
return new Response(
JSON.stringify({ error: 'Invalid input' }),
{ status: 400 }
);
}
⚠️ This skill MUST update tracking files PROGRESSIVELY during execution, NOT just at the end.
DO NOT batch all writes at the end. Instead:
.sb-pentest-audit.log.sb-pentest-context.jsonThis ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved.
Update.sb-pentest-context.json with results:
{ "functions_audit": { "timestamp": "...", "functions_found": 5, "findings": [ ... ] } }
Log to.sb-pentest-audit.log:
[TIMESTAMP] [supabase-audit-functions] [START] Auditing Edge Functions [TIMESTAMP] [supabase-audit-functions] [FINDING] P0: IDOR in get-user-data [TIMESTAMP] [supabase-audit-functions] [CONTEXT_UPDATED] .sb-pentest-context.json updated
If files don't exist , create them before writing.
FAILURE TO UPDATE CONTEXT FILES IS NOT ACCEPTABLE.
📁 Evidence Directory: .sb-pentest-evidence/07-functions-audit/
| File | Content |
|---|---|
discovered-functions.json | List of discovered Edge Functions |
function-tests/[name].json | Test results per function |
{
"evidence_id": "FN-001",
"timestamp": "2025-01-31T11:10:00Z",
"category": "functions-audit",
"type": "idor_vulnerability",
"severity": "P0",
"function": "get-user-data",
"endpoint": "https://abc123def.supabase.co/functions/v1/get-user-data",
"tests": [
{
"test_name": "auth_required",
"request": {
"method": "GET",
"headers": {},
"curl_command": "curl '$URL/functions/v1/get-user-data'"
},
"response": {"status": 401},
"result": "PASS"
},
{
"test_name": "idor_test",
"description": "As user A, request user B's data",
"request": {
"method": "GET",
"url": "$URL/functions/v1/get-user-data?user_id=user-b-id",
"headers": {"Authorization": "Bearer [USER_A_TOKEN]"},
"curl_command": "curl '$URL/functions/v1/get-user-data?user_id=user-b-id' -H 'Authorization: Bearer [USER_A_TOKEN]'"
},
"response": {
"status": 200,
"body": {"id": "user-b-id", "email": "[REDACTED]", "data": "[REDACTED]"}
},
"result": "VULNERABLE",
"impact": "Any authenticated user can access any other user's data"
}
],
"remediation": "Add ownership check: if (user_id !== jwt_user.id) return 403"
}
{
"evidence_id": "FN-002",
"timestamp": "2025-01-31T11:15:00Z",
"category": "functions-audit",
"type": "privilege_escalation",
"severity": "P0",
"function": "admin-panel",
"test": {
"description": "Regular user accessing admin function",
"request": {
"method": "GET",
"headers": {"Authorization": "Bearer [REGULAR_USER_TOKEN]"},
"curl_command": "curl '$URL/functions/v1/admin-panel' -H 'Authorization: Bearer [REGULAR_USER_TOKEN]'"
},
"response": {
"status": 200,
"body": {"admin_data": "[REDACTED]"}
},
"result": "VULNERABLE",
"impact": "Any authenticated user has admin access"
}
}
supabase-audit-rpc — Database functions (different from Edge Functions)supabase-audit-auth-config — Auth configurationsupabase-report — Include in final reportWeekly Installs
103
Repository
GitHub Stars
33
First Seen
Jan 31, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykFail
Installed on
claude-code85
codex79
opencode78
gemini-cli75
github-copilot70
cursor70
Supabase Postgres 最佳实践指南 - 8大类别性能优化规则与SQL示例
78,800 周安装