supabase-audit-tables-read by yoanbernabeu/supabase-pentest-skills
npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-audit-tables-read🔴 关键要求:必须进行渐进式文件更新
你必须在执行过程中就写入上下文文件,而不是等到最后才写。
- 测试完每个表后立即写入
.sb-pentest-context.json- 在每个测试前后记录到
.sb-pentest-audit.log- 切勿等到技能执行完毕才更新文件
- 如果技能崩溃或被中断,所有之前的发现必须已经保存
这不是可选项。未能进行渐进式写入是严重错误。
此技能尝试从暴露的表中读取数据,以确定实际可以访问哪些信息。
supabase-audit-tables-list)该技能对每个暴露的表执行 SELECT 查询:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
GET https://[project].supabase.co/rest/v1/[table]?select=*&limit=5
Authorization: Bearer [anon-key]
重要提示: 这是只读操作。不会修改或删除任何数据。
| 模式 | 描述 | 查询 |
|---|---|---|
| 快速 | 每个表的前 5 行 | ?limit=5 |
| 抽样 | 跨表的随机样本 | ?limit=10&order=random |
| 计数 | 仅统计行数,不获取数据 | HEAD 请求 |
测试对暴露表的读取访问权限
统计所有表中可访问的行数(不检索数据)
测试对 users 表的读取访问权限
═══════════════════════════════════════════════════════════
数据访问测试结果
═══════════════════════════════════════════════════════════
测试模式:快速(每个表 5 行)
测试表数:8
─────────────────────────────────────────────────────────
按表统计结果
─────────────────────────────────────────────────────────
1. users
状态:🔴 P0 - 数据已暴露
检索到的行数:5(共 1,247 行)
样本数据:
┌─────────────────────────────────────────────────────┐
│ id: 550e8400-e29b-41d4-a716-446655440001 │
│ email: john.doe@example.com ← PII 已暴露 │
│ name: John Doe ← PII 已暴露 │
│ avatar_url: https://... │
│ created_at: 2025-01-15T10:30:00Z │
└─────────────────────────────────────────────────────┘
发现:无需认证即可访问用户邮箱和姓名
2. profiles
状态:🟠 P1 - 部分访问
检索到的行数:5
备注:仅返回公共字段(RLS 部分生效)
可见列:id, bio, website
被阻止的列:user_id, social_links, private_notes
3. posts
状态:✅ 预期访问
检索到的行数:5
备注:仅返回 published=true 的帖子(RLS 生效)
数据:公共内容,访问级别适当
4. orders
状态:✅ 已阻止
响应:403 Forbidden
消息:"new row violates row-level security policy"
备注:RLS 正确阻止了访问
5. api_keys
状态:✅ 已阻止
响应:403 Forbidden
备注:RLS 正确保护了密钥
6. products
状态:✅ 预期访问
检索到的行数:5
备注:公共目录数据,访问级别适当
7. comments
状态:🟠 P1 - 数据超出预期
检索到的行数:5
问题:user_id 列暴露(可与 users 表关联)
建议:使用视图隐藏 user_id
8. settings
状态:🔴 P0 - 敏感数据已暴露
检索到的行数:3
样本数据:
┌─────────────────────────────────────────────────────┐
│ key: stripe_webhook_secret │
│ value: whsec_xxxxxxxxxxxx ← 密钥已暴露 │
└─────────────────────────────────────────────────────┘
发现:可访问的表中存在应用密钥!
─────────────────────────────────────────────────────────
摘要
─────────────────────────────────────────────────────────
P0(严重):2 个表暴露了敏感数据
P1(高):2 个表存在部分/意外暴露
已阻止:2 个表受到适当保护
预期:2 个表具有适当的公共访问权限
可访问的总行数:暴露的表中共 1,892 行
立即行动:
1. 修复 'settings' 表 - 从公开范围移除或添加 RLS
2. 修复 'users' 表 - 添加 RLS 以保护邮箱/姓名
3. 审查 'comments' 表以隐藏用户关联
═══════════════════════════════════════════════════════════
| 状态 | 严重性 | 标准 |
|---|---|---|
| 🔴 数据已暴露 | P0 | 敏感数据(PII、密钥、财务信息)可访问 |
| 🟠 部分访问 | P1 | 数据超出预期,但不严重 |
| 🟡 意外 | P2 | 可访问但风险低的数据 |
| ✅ 已阻止 | - | RLS 正确阻止了访问 |
| ✅ 预期 | - | 公共数据,访问级别适当 |
该技能识别敏感数据类型:
| 类型 | 模式 | 暴露时的严重性 |
|---|---|---|
| PII | email, phone, name, address | P0 |
| 财务信息 | amount, total, card, payment | P0 |
| 密钥 | key, secret, token, password | P0 |
| 认证信息 | user_id, session, jwt | P1 |
| 元数据 | created_at, updated_at | P2 |
{
"data_access": {
"timestamp": "2025-01-31T10:30:00Z",
"tables_tested": 8,
"summary": {
"p0_exposed": 2,
"p1_partial": 2,
"blocked": 2,
"expected": 2
},
"results": [
{
"table": "users",
"status": "exposed",
"severity": "P0",
"rows_accessible": 1247,
"sensitive_columns": ["email", "name"],
"sample_redacted": true
},
{
"table": "settings",
"status": "exposed",
"severity": "P0",
"rows_accessible": 3,
"sensitive_data_types": ["secrets"],
"finding": "Application secrets exposed"
}
],
"total_rows_accessible": 1892
}
}
[2025-01-31T10:30:00Z] READ_TEST_START tables=8
[2025-01-31T10:30:01Z] READ_TEST table=users status=200 rows=5 severity=P0
[2025-01-31T10:30:01Z] READ_TEST table=orders status=403 severity=none
[2025-01-31T10:30:02Z] READ_TEST_COMPLETE exposed=4 blocked=2
-- 启用 RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- 仅认证用户能看到自己的数据
CREATE POLICY "Users see own data"
ON users FOR SELECT
USING (auth.uid() = id);
-- 或者创建一个列受限的公共视图
CREATE VIEW public.users_public AS
SELECT id, avatar_url, created_at FROM users;
-- 完全从公共访问中移除
REVOKE ALL ON TABLE settings FROM anon, authenticated;
-- 仅通过 Edge Functions 访问
-- 在你的 Edge Function 中:
const { data } = await supabaseAdmin
.from('settings')
.select('*')
.eq('key', 'stripe_webhook_secret')
.single()
-- 仅对已发布内容启用 RLS
CREATE POLICY "Public sees published posts"
ON posts FOR SELECT
USING (published = true);
-- 作者能看到自己的草稿
CREATE POLICY "Authors see own posts"
ON posts FOR SELECT
USING (auth.uid() = author_id);
❌ 问题: 所有表都返回 403 ✅ 解决方案: RLS 可能限制过严或匿名密钥无效。从安全角度看这实际上是好事。
❌ 问题: 结果为空但没有错误 ✅ 解决方案: RLS 过滤了所有行。表结构暴露但无数据。
❌ 问题: 大表超时 ✅ 解决方案: 使用计数模式或减少限制行数。
⚠️ 此技能必须在执行过程中渐进式更新跟踪文件,而不是仅在最后更新。
切勿在最后批量写入所有内容。而是:
.sb-pentest-audit.log.sb-pentest-context.json.sb-pentest-audit.log这确保了如果技能被中断、崩溃或超时,所有到该点为止的发现都能被保存。
更新.sb-pentest-context.json 并包含结果:
{ "data_access": { "timestamp": "...", "tables_tested": 8, "summary": { "p0_exposed": 2, ... }, "results": [ ... ], "total_rows_accessible": 1892 } }
记录到.sb-pentest-audit.log:
[TIMESTAMP] [supabase-audit-tables-read] [START] Testing data access [TIMESTAMP] [supabase-audit-tables-read] [FINDING] P0: users table exposed [TIMESTAMP] [supabase-audit-tables-read] [CONTEXT_UPDATED] .sb-pentest-context.json updated
如果文件不存在,在写入前创建它们。
未能更新上下文文件是不可接受的。
📁 证据目录: .sb-pentest-evidence/03-api-audit/data-samples/
| 文件 | 内容 |
|---|---|
data-samples/[table]-sample.json | 每个可访问表的样本数据 |
data-samples/[table]-blocked.json | 访问被阻止的证据(403 响应) |
{
"evidence_id": "API-READ-001",
"timestamp": "2025-01-31T10:20:00Z",
"category": "api-audit",
"type": "data_access",
"severity": "P0",
"finding_id": "P0-002",
"table": "users",
"request": {
"method": "GET",
"url": "https://abc123def.supabase.co/rest/v1/users?select=*&limit=5",
"headers": {
"apikey": "[REDACTED]",
"Authorization": "Bearer [REDACTED]"
},
"curl_command": "curl -s 'https://abc123def.supabase.co/rest/v1/users?select=*&limit=5' -H 'apikey: $ANON_KEY' -H 'Authorization: Bearer $ANON_KEY'"
},
"response": {
"status": 200,
"headers": {
"content-range": "0-4/1247"
},
"total_rows": 1247,
"sample_data": [
{
"id": "550e8400-e29b-41d4-...",
"email": "[REDACTED]@example.com",
"name": "[REDACTED]",
"created_at": "2025-01-15T10:30:00Z"
}
],
"data_redacted": true
},
"analysis": {
"severity": "P0",
"pii_exposed": ["email", "name"],
"total_records_accessible": 1247,
"authentication_required": false
}
}
{
"evidence_id": "API-READ-002",
"timestamp": "2025-01-31T10:21:00Z",
"table": "orders",
"severity": null,
"response": {
"status": 403,
"body": {"message": "new row violates row-level security policy"}
},
"analysis": {
"rls_working": true,
"access_blocked": true
}
}
# === 数据访问测试 ===
# 测试:Users 表访问
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer $ANON_KEY"
# 测试:Orders 表访问(应被阻止)
curl -s "$SUPABASE_URL/rest/v1/orders?select=*&limit=5" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer $ANON_KEY"
supabase-audit-tables-list — 首先列出表supabase-audit-rls — 深入分析 RLS 策略supabase-report — 生成完整报告每周安装次数
96
仓库
GitHub Stars
33
首次出现
Jan 31, 2026
安全审计
安装在
claude-code77
codex71
opencode70
gemini-cli67
github-copilot62
cursor62
🔴 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 table tested- Log to
.sb-pentest-audit.logBEFORE and AFTER each 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 attempts to read data from exposed tables to determine what information is actually accessible.
supabase-audit-tables-list if needed)The skill performs SELECT queries on each exposed table:
GET https://[project].supabase.co/rest/v1/[table]?select=*&limit=5
Authorization: Bearer [anon-key]
Important: This is READ-ONLY. No data is modified or deleted.
| Mode | Description | Queries |
|---|---|---|
| Quick | First 5 rows from each table | ?limit=5 |
| Sample | Random sample across tables | ?limit=10&order=random |
| Count | Just row counts, no data | HEAD request |
Test read access on exposed tables
Count accessible rows in all tables (no data retrieval)
Test read access on the users table
═══════════════════════════════════════════════════════════
DATA ACCESS TEST RESULTS
═══════════════════════════════════════════════════════════
Test Mode: Quick (5 rows per table)
Tables Tested: 8
─────────────────────────────────────────────────────────
Results by Table
─────────────────────────────────────────────────────────
1. users
Status: 🔴 P0 - DATA EXPOSED
Rows Retrieved: 5 (of 1,247 total)
Sample Data:
┌─────────────────────────────────────────────────────┐
│ id: 550e8400-e29b-41d4-a716-446655440001 │
│ email: john.doe@example.com ← PII EXPOSED │
│ name: John Doe ← PII EXPOSED │
│ avatar_url: https://... │
│ created_at: 2025-01-15T10:30:00Z │
└─────────────────────────────────────────────────────┘
Finding: User emails and names accessible without auth
2. profiles
Status: 🟠 P1 - PARTIAL ACCESS
Rows Retrieved: 5
Note: Only public fields returned (RLS working partially)
Columns Visible: id, bio, website
Columns Blocked: user_id, social_links, private_notes
3. posts
Status: ✅ EXPECTED ACCESS
Rows Retrieved: 5
Note: Only published=true posts returned (RLS working)
Data: Public content, appropriate access level
4. orders
Status: ✅ BLOCKED
Response: 403 Forbidden
Message: "new row violates row-level security policy"
Note: RLS properly blocking access
5. api_keys
Status: ✅ BLOCKED
Response: 403 Forbidden
Note: RLS properly protecting secrets
6. products
Status: ✅ EXPECTED ACCESS
Rows Retrieved: 5
Note: Public catalog data, appropriate access
7. comments
Status: 🟠 P1 - MORE DATA THAN EXPECTED
Rows Retrieved: 5
Issue: user_id column exposed (can correlate to users)
Recommendation: Use a view to hide user_id
8. settings
Status: 🔴 P0 - SENSITIVE DATA EXPOSED
Rows Retrieved: 3
Sample Data:
┌─────────────────────────────────────────────────────┐
│ key: stripe_webhook_secret │
│ value: whsec_xxxxxxxxxxxx ← SECRET EXPOSED │
└─────────────────────────────────────────────────────┘
Finding: Application secrets in accessible table!
─────────────────────────────────────────────────────────
Summary
─────────────────────────────────────────────────────────
P0 (Critical): 2 tables with sensitive data exposed
P1 (High): 2 tables with partial/unexpected exposure
Blocked: 2 tables properly protected
Expected: 2 tables with appropriate public access
Total Rows Accessible: 1,892 across exposed tables
Immediate Actions:
1. Fix 'settings' table - remove from public or add RLS
2. Fix 'users' table - add RLS to protect email/name
3. Review 'comments' to hide user correlation
═══════════════════════════════════════════════════════════
| Status | Severity | Criteria |
|---|---|---|
| 🔴 DATA EXPOSED | P0 | Sensitive data (PII, secrets, financial) accessible |
| 🟠 PARTIAL ACCESS | P1 | More data than expected, but not critical |
| 🟡 UNEXPECTED | P2 | Accessible but low-risk data |
| ✅ BLOCKED | - | RLS properly preventing access |
| ✅ EXPECTED | - | Public data, appropriate access |
The skill identifies sensitive data types:
| Type | Patterns | Severity if Exposed |
|---|---|---|
| PII | email, phone, name, address | P0 |
| Financial | amount, total, card, payment | P0 |
| Secrets | key, secret, token, password | P0 |
| Auth | user_id, session, jwt | P1 |
| Metadata | created_at, updated_at | P2 |
{
"data_access": {
"timestamp": "2025-01-31T10:30:00Z",
"tables_tested": 8,
"summary": {
"p0_exposed": 2,
"p1_partial": 2,
"blocked": 2,
"expected": 2
},
"results": [
{
"table": "users",
"status": "exposed",
"severity": "P0",
"rows_accessible": 1247,
"sensitive_columns": ["email", "name"],
"sample_redacted": true
},
{
"table": "settings",
"status": "exposed",
"severity": "P0",
"rows_accessible": 3,
"sensitive_data_types": ["secrets"],
"finding": "Application secrets exposed"
}
],
"total_rows_accessible": 1892
}
}
[2025-01-31T10:30:00Z] READ_TEST_START tables=8
[2025-01-31T10:30:01Z] READ_TEST table=users status=200 rows=5 severity=P0
[2025-01-31T10:30:01Z] READ_TEST table=orders status=403 severity=none
[2025-01-31T10:30:02Z] READ_TEST_COMPLETE exposed=4 blocked=2
-- Enable RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- Only authenticated users see their own data
CREATE POLICY "Users see own data"
ON users FOR SELECT
USING (auth.uid() = id);
-- Or create a public view with limited columns
CREATE VIEW public.users_public AS
SELECT id, avatar_url, created_at FROM users;
-- Remove from public access entirely
REVOKE ALL ON TABLE settings FROM anon, authenticated;
-- Access only via Edge Functions
-- In your Edge Function:
const { data } = await supabaseAdmin
.from('settings')
.select('*')
.eq('key', 'stripe_webhook_secret')
.single()
-- RLS for published content only
CREATE POLICY "Public sees published posts"
ON posts FOR SELECT
USING (published = true);
-- Authors see their own drafts
CREATE POLICY "Authors see own posts"
ON posts FOR SELECT
USING (auth.uid() = author_id);
❌ Problem: All tables return 403 ✅ Solution: RLS may be too restrictive or anon key invalid. This is actually good from a security standpoint.
❌ Problem: Empty results but no error ✅ Solution: RLS is filtering all rows. Table structure is exposed but no data.
❌ Problem: Timeout on large tables ✅ Solution: Use count mode or reduce limit.
⚠️ 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.json with results.sb-pentest-audit.logThis 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:
{ "data_access": { "timestamp": "...", "tables_tested": 8, "summary": { "p0_exposed": 2, ... }, "results": [ ... ], "total_rows_accessible": 1892 } }
Log to.sb-pentest-audit.log:
[TIMESTAMP] [supabase-audit-tables-read] [START] Testing data access [TIMESTAMP] [supabase-audit-tables-read] [FINDING] P0: users table exposed [TIMESTAMP] [supabase-audit-tables-read] [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/03-api-audit/data-samples/
| File | Content |
|---|---|
data-samples/[table]-sample.json | Sample data from each accessible table |
data-samples/[table]-blocked.json | Proof of blocked access (403 response) |
{
"evidence_id": "API-READ-001",
"timestamp": "2025-01-31T10:20:00Z",
"category": "api-audit",
"type": "data_access",
"severity": "P0",
"finding_id": "P0-002",
"table": "users",
"request": {
"method": "GET",
"url": "https://abc123def.supabase.co/rest/v1/users?select=*&limit=5",
"headers": {
"apikey": "[REDACTED]",
"Authorization": "Bearer [REDACTED]"
},
"curl_command": "curl -s 'https://abc123def.supabase.co/rest/v1/users?select=*&limit=5' -H 'apikey: $ANON_KEY' -H 'Authorization: Bearer $ANON_KEY'"
},
"response": {
"status": 200,
"headers": {
"content-range": "0-4/1247"
},
"total_rows": 1247,
"sample_data": [
{
"id": "550e8400-e29b-41d4-...",
"email": "[REDACTED]@example.com",
"name": "[REDACTED]",
"created_at": "2025-01-15T10:30:00Z"
}
],
"data_redacted": true
},
"analysis": {
"severity": "P0",
"pii_exposed": ["email", "name"],
"total_records_accessible": 1247,
"authentication_required": false
}
}
{
"evidence_id": "API-READ-002",
"timestamp": "2025-01-31T10:21:00Z",
"table": "orders",
"severity": null,
"response": {
"status": 403,
"body": {"message": "new row violates row-level security policy"}
},
"analysis": {
"rls_working": true,
"access_blocked": true
}
}
# === DATA ACCESS TESTS ===
# Test: Users table access
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer $ANON_KEY"
# Test: Orders table access (should be blocked)
curl -s "$SUPABASE_URL/rest/v1/orders?select=*&limit=5" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer $ANON_KEY"
supabase-audit-tables-list — List tables firstsupabase-audit-rls — Deep dive into RLS policiessupabase-report — Generate full reportWeekly Installs
96
Repository
GitHub Stars
33
First Seen
Jan 31, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
claude-code77
codex71
opencode70
gemini-cli67
github-copilot62
cursor62
Azure PostgreSQL 无密码身份验证配置指南:Entra ID 迁移与访问管理
34,800 周安装
调试向导 - 系统化代码调试方法,Python/JavaScript/Go调试工具与工作流程
1,200 周安装
Motion Vue (motion-v) - Vue 3/Nuxt 动画库 | 硬件加速、声明式动画、手势交互
1,200 周安装
React Native 专家技能:构建高性能跨平台移动应用的完整指南与最佳实践
1,200 周安装
全栈安全开发指南 - Fullstack Guardian 安全编码与三视角设计实践
1,300 周安装
CLI开发者指南:Node.js/Python/Go命令行工具开发全流程与最佳实践
1,200 周安装
Claude AI前端项目构建器:React + TypeScript + Vite + Parcel打包成单HTML文件
1,200 周安装