T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:33
- Finding
- Unauthenticated Privileged Supabase Data Proxy with Caller-Controlled Query Selection<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 33-46 **Vulnerability Type**: Unauthenticated access to a privileged database proxy **Risk Level**: High ### Vulnerable Code ```python SUPABASE_KEY = os.environ["SUPABASE_SERVICE_KEY"] HEADERS = {"apikey": SUPABASE_KEY, "Authorization": f"Bearer {SUPABASE_KEY}"} app = FastAPI() @app.get("/api/mc/{table}") async def get_table(table: str, select: str = "*", limit: int = 100, offset: int = 0): allowed = {"ai_agents", "skills", "knowledge_vault", "tools", "workflows"} if table not in allowed: raise HTTPException(403, "Table not allowed") url = f"{SUPABASE_URL}/rest/v1/{table}?select={select}&limit={limit}&offset={offset}" async with httpx.AsyncClient() as client: r = await client.get(url, headers={**HEADERS, "Prefer": "count=exact"}) return r.json() ``` ### Technical Analysis The documented FastAPI endpoint does not perform authentication or authorization before proxying requests to Supabase with `SUPABASE_SERVICE_KEY`. A Supabase service-role credential is typically highly privileged and may bypass row-level security policies. Consequently, the table allowlist alone does not adequately protect individual records or sensitive columns. The endpoint also defaults `select` to `*`, exposing every column in an allowed table. This conflicts with the document's later recommendation to use lightweight, explicit column selections and may disclose large or sensitive fields such as prompts or knowledge records. Additionally, the caller-controlled `select` value is interpolated directly into the URL. Because it is not passed through `httpx` query parameter encoding or restricted to an allowlist of columns, a value containing query delimiters such as `&` could introduce additional PostgREST parameters and alter the upstream request. ### Attack Path 1. An attacker identifies a deployed dashboard API containing `/api/mc/{table}`. 2. The attacker requests an allowl ...[truncated 1280 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require authentication for every dashboard API endpoint. 2. Enforce role-based authorization for each table, operation, row, and column. 3. Avoid using a Supabase service-role credential for user-facing read requests. Prefer a restricted database role or forward a validated user token so row-level security remains effective. 4. Define fixed column allowlists for each permitted table and reject all unapproved columns. 5. Remove the `select="*"` default. Use explicit, minimal defaults that exclude sensitive or large fields. 6. Construct upstream requests through the `params` argument so `httpx` safely encodes query values: ```python params = { "select": validated_select, "limit": validated_limit, "offset": validated_offset, } r = await client.get( f"{SUPABASE_URL}/rest/v1/{table}", params=params, headers={**HEADERS, "Prefer": "count=exact"}, ) ``` 7. Validate and bound `limit` and `offset` to prevent bulk extraction and resource exhaustion. 8. Return controlled errors after checking the upstream status instead of forwarding arbitrary upstream response bodies. 9. Add access logging, rate limiting, and monitoring for unusual pagination or bulk table access. ]]>
