supabase-audit-tables-read

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Table Data Access Test

数据表访问测试

🔴 CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED
You MUST write to context files AS YOU GO, not just at the end.
  • Write to
    .sb-pentest-context.json
    IMMEDIATELY after each table tested
  • Log to
    .sb-pentest-audit.log
    BEFORE 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.
🔴 关键要求:需逐步更新文件
你必须在操作过程中写入上下文文件,而不是仅在最后统一写入。
  • 测试完每个表后立即写入
    .sb-pentest-context.json
  • 每次测试前后都要记录到
    .sb-pentest-audit.log
  • 禁止等到技能完成后再批量更新文件
  • 如果技能崩溃或被中断,所有已有的测试结果必须已保存
此要求为强制性规定,未逐步更新文件属于严重错误。
本技能尝试从暴露的表中读取数据,以确定实际可访问的信息范围。

When to Use This Skill

何时使用该技能

  • After listing tables, to verify actual access
  • To test RLS policy effectiveness
  • To assess the severity of data exposure
  • To document exactly what data can be retrieved
  • 列出表之后,验证实际访问权限
  • 测试RLS策略的有效性
  • 评估数据泄露的严重程度
  • 记录可检索到的具体数据内容

Prerequisites

前置条件

  • Tables listed (auto-invokes
    supabase-audit-tables-list
    if needed)
  • Anon key available
  • 已列出所有表(如果未列出,会自动调用
    supabase-audit-tables-list
  • 已获取Anon密钥

How It Works

工作原理

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.
本技能会对每个暴露的表执行SELECT查询:
GET https://[project].supabase.co/rest/v1/[table]?select=*&limit=5
Authorization: Bearer [anon-key]
重要说明: 本操作仅为只读,不会修改或删除任何数据。

Test Modes

测试模式

ModeDescriptionQueries
QuickFirst 5 rows from each table
?limit=5
SampleRandom sample across tables
?limit=10&order=random
CountJust row counts, no data
HEAD
request
模式描述查询参数
快速模式每个表的前5行数据
?limit=5
抽样模式跨表随机抽样
?limit=10&order=random
计数模式仅统计行数,不获取数据
HEAD
请求

Usage

使用方法

Basic Read Test

基础读取测试

Test read access on exposed tables
测试暴露表的读取权限

Quick Count Only

仅快速计数

Count accessible rows in all tables (no data retrieval)
统计所有表的可访问行数(不获取数据)

Specific Table

指定表测试

Test read access on the users table
测试users表的读取权限

Output Format

输出格式

═══════════════════════════════════════════════════════════
 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

═══════════════════════════════════════════════════════════
═══════════════════════════════════════════════════════════
 数据访问测试结果
═══════════════════════════════════════════════════════════

 测试模式:快速模式(每个表5行数据)
 测试表数量:8个

 ─────────────────────────────────────────────────────────
 各表测试结果
 ─────────────────────────────────────────────────────────

 1. users
    状态:🔴 P0 - 数据已泄露
    检索到的行数:5行(总计1247行)
    样本数据:
    ┌─────────────────────────────────────────────────────┐
    │ id: 550e8400-e29b-41d4-a716-446655440001           │
    │ email: john.doe@example.com ← 个人身份信息已泄露          │
    │ name: John Doe ← 个人身份信息已泄露                       │
    │ 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列已暴露(可关联到用户信息)
    建议:使用视图隐藏user_id

 8. settings
    状态:🔴 P0 - 敏感数据已泄露
    检索到的行数:3行
    样本数据:
    ┌─────────────────────────────────────────────────────┐
    │ key: stripe_webhook_secret                          │
    │ value: whsec_xxxxxxxxxxxx ← 密钥已泄露         │
    └─────────────────────────────────────────────────────┘
    发现:可访问的表中包含应用密钥!

 ─────────────────────────────────────────────────────────
 总结
 ─────────────────────────────────────────────────────────

 P0(严重):2个表存在敏感数据泄露
 P1(高风险):2个表存在部分/意外数据暴露
 已屏蔽:2个表被正确保护
 符合预期:2个表的公开访问权限正常

 可访问总行数:所有暴露表总计1892行

 立即执行的操作:
 1. 修复settings表 - 移除公开权限或添加RLS
 2. 修复users表 - 添加RLS以保护邮箱/姓名
 3. 检查comments表,隐藏用户关联信息

═══════════════════════════════════════════════════════════

Severity Assessment

严重程度评估

StatusSeverityCriteria
🔴 DATA EXPOSEDP0Sensitive data (PII, secrets, financial) accessible
🟠 PARTIAL ACCESSP1More data than expected, but not critical
🟡 UNEXPECTEDP2Accessible but low-risk data
✅ BLOCKED-RLS properly preventing access
✅ EXPECTED-Public data, appropriate access
状态严重等级判断标准
🔴 数据已泄露P0敏感数据(个人身份信息、密钥、财务数据)可被访问
🟠 部分访问权限P1访问数据超出预期,但不属于严重级别
🟡 意外访问P2可访问但低风险的数据
✅ 已被屏蔽-RLS正确阻止访问
✅ 预期内访问-公开数据,访问权限符合预期

Data Classification

数据分类

The skill identifies sensitive data types:
TypePatternsSeverity if Exposed
PIIemail, phone, name, addressP0
Financialamount, total, card, paymentP0
Secretskey, secret, token, passwordP0
Authuser_id, session, jwtP1
Metadatacreated_at, updated_atP2
本技能可识别以下敏感数据类型:
类型识别模式泄露后的严重程度
个人身份信息(PII)邮箱、电话、姓名、地址P0
财务数据金额、总计、银行卡、支付信息P0
密钥key、secret、token、passwordP0
认证信息user_id、session、jwtP1
元数据created_at、updated_atP2

Context Output

上下文输出

json
{
  "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
  }
}
json
{
  "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
  }
}

Audit Log Entry

审计日志条目

[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
[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

Remediation Examples

修复示例

For User Tables

用户表修复

sql
-- 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;
sql
-- 启用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;

For Settings Tables

设置表修复

sql
-- 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()
sql
-- 完全移除公开访问权限
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()

For Content Tables

内容表修复

sql
-- 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);
sql
-- 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);

Common Issues

常见问题

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.
问题:所有表都返回403解决方案:RLS可能过于严格或Anon密钥无效。从安全角度来看,这实际上是良好的表现。
问题:返回空结果但无错误解决方案:RLS过滤了所有行。表结构已暴露,但无数据可访问。
问题:大表测试超时解决方案:使用计数模式或减少查询行数限制。

MANDATORY: Progressive Context File Updates

强制性要求:逐步更新上下文文件

⚠️ This skill MUST update tracking files PROGRESSIVELY during execution, NOT just at the end.
⚠️ 本技能必须在执行过程中逐步更新跟踪文件,而非仅在最后统一更新

Critical Rule: Write As You Go

核心规则:边操作边写入

DO NOT batch all writes at the end. Instead:
  1. Before testing each table → Log the action to
    .sb-pentest-audit.log
  2. After each table tested → Immediately update
    .sb-pentest-context.json
    with results
  3. After each finding → Log the severity to
    .sb-pentest-audit.log
This ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved.
禁止最后批量写入。应遵循以下步骤:
  1. 测试每个表之前 → 将操作记录到
    .sb-pentest-audit.log
  2. 测试每个表之后 → 立即将结果更新到
    .sb-pentest-context.json
  3. 发现每个问题之后 → 将严重程度记录到
    .sb-pentest-audit.log
这样可确保即使技能中断、崩溃或超时,所有已完成的测试结果都已被保存。

Required Actions (Progressive)

必须执行的逐步操作

  1. Update
    .sb-pentest-context.json
    with results:
    json
    {
      "data_access": {
        "timestamp": "...",
        "tables_tested": 8,
        "summary": { "p0_exposed": 2, ... },
        "results": [ ... ],
        "total_rows_accessible": 1892
      }
    }
  2. 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
  3. If files don't exist, create them before writing.
FAILURE TO UPDATE CONTEXT FILES IS NOT ACCEPTABLE.
  1. 更新
    .sb-pentest-context.json
    以保存结果:
    json
    {
      "data_access": {
        "timestamp": "...",
        "tables_tested": 8,
        "summary": { "p0_exposed": 2, ... },
        "results": [ ... ],
        "total_rows_accessible": 1892
      }
    }
  2. 记录到
    .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
  3. 如果文件不存在,在写入前先创建。
未更新上下文文件的行为是不被接受的。

MANDATORY: Evidence Collection

强制性要求:收集证据

📁 Evidence Directory:
.sb-pentest-evidence/03-api-audit/data-samples/
📁 证据目录:
.sb-pentest-evidence/03-api-audit/data-samples/

Evidence Files to Create

需要创建的证据文件

FileContent
data-samples/[table]-sample.json
Sample data from each accessible table
data-samples/[table]-blocked.json
Proof of blocked access (403 response)
文件内容
data-samples/[table]-sample.json
每个可访问表的样本数据
data-samples/[table]-blocked.json
访问被屏蔽的证明(403响应)

Evidence Format (Data Exposed)

数据泄露的证据格式

json
{
  "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
  }
}
json
{
  "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 Format (Properly Blocked)

访问被正确屏蔽的证据格式

json
{
  "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
  }
}
json
{
  "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
  }
}

Add to curl-commands.sh

添加到curl-commands.sh

bash
undefined
bash
undefined

=== 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"
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"
undefined
curl -s "$SUPABASE_URL/rest/v1/orders?select=*&limit=5"
-H "apikey: $ANON_KEY"
-H "Authorization: Bearer $ANON_KEY"
undefined

Related Skills

相关技能

  • supabase-audit-tables-list
    — List tables first
  • supabase-audit-rls
    — Deep dive into RLS policies
  • supabase-report
    — Generate full report
  • supabase-audit-tables-list
    — 先列出所有表
  • supabase-audit-rls
    — 深入分析RLS策略
  • supabase-report
    — 生成完整报告