@rip-user/rls-debugger-mcp
Version:
AI-powered MCP server for debugging Supabase Row Level Security policies with Claude structured outputs
115 lines • 3.55 kB
JavaScript
/**
* SQL query to fetch all RLS policies from PostgreSQL system tables
*/
export const GET_ALL_RLS_POLICIES = `
SELECT
nsp.nspname AS schemaname,
rel.relname AS tablename,
pol.polname AS policyname,
CASE pol.polpermissive
WHEN true THEN 'PERMISSIVE'
WHEN false THEN 'RESTRICTIVE'
END AS polpermissive,
CASE
WHEN cardinality(pol.polroles) > 0 THEN pg_get_userbyid(pol.polroles[1])
ELSE 'public'
END AS policyrole,
CASE pol.polcmd
WHEN '*' THEN 'ALL'
WHEN 'r' THEN 'SELECT'
WHEN 'a' THEN 'INSERT'
WHEN 'w' THEN 'UPDATE'
WHEN 'd' THEN 'DELETE'
END AS polcmd,
pg_get_expr(pol.polqual, pol.polrelid) AS policyqual,
pg_get_expr(pol.polwithcheck, pol.polrelid) AS policywithcheck
FROM pg_catalog.pg_policy pol
JOIN pg_catalog.pg_class rel ON pol.polrelid = rel.oid
JOIN pg_catalog.pg_namespace nsp ON rel.relnamespace = nsp.oid
WHERE nsp.nspname = 'public'
ORDER BY rel.relname, pol.polname;
`;
/**
* SQL query to get policies for a specific table
*/
export function getTablePoliciesQuery(tableName) {
return `
SELECT
nsp.nspname AS schemaname,
rel.relname AS tablename,
pol.polname AS policyname,
CASE pol.polpermissive
WHEN true THEN 'PERMISSIVE'
WHEN false THEN 'RESTRICTIVE'
END AS polpermissive,
CASE
WHEN cardinality(pol.polroles) > 0 THEN pg_get_userbyid(pol.polroles[1])
ELSE 'public'
END AS policyrole,
CASE pol.polcmd
WHEN '*' THEN 'ALL'
WHEN 'r' THEN 'SELECT'
WHEN 'a' THEN 'INSERT'
WHEN 'w' THEN 'UPDATE'
WHEN 'd' THEN 'DELETE'
END AS polcmd,
pg_get_expr(pol.polqual, pol.polrelid) AS policyqual,
pg_get_expr(pol.polwithcheck, pol.polrelid) AS policywithcheck
FROM pg_catalog.pg_policy pol
JOIN pg_catalog.pg_class rel ON pol.polrelid = rel.oid
JOIN pg_catalog.pg_namespace nsp ON rel.relnamespace = nsp.oid
WHERE nsp.nspname = 'public' AND rel.relname = '${tableName}'
ORDER BY pol.polname;
`;
}
/**
* Fetch all RLS policies from Supabase
*/
export async function fetchAllPolicies(supabase) {
// Use raw SQL query directly
const { data, error } = await supabase.rpc('exec_sql', {
query: GET_ALL_RLS_POLICIES
});
if (error) {
// Try alternative approach using information_schema
const altQuery = `
SELECT
schemaname,
tablename,
policyname
FROM pg_policies
WHERE schemaname = 'public';
`;
const { data: altData, error: altError } = await supabase.rpc('exec_sql', {
query: altQuery
});
if (altError) {
throw new Error(`Failed to fetch RLS policies: ${error.message || altError.message}`);
}
return altData || [];
}
return data || [];
}
/**
* Fetch policies for a specific table
*/
export async function fetchTablePolicies(supabase, tableName) {
const allPolicies = await fetchAllPolicies(supabase);
return allPolicies.filter(p => p.tablename === tableName);
}
/**
* Check if a table has RLS enabled
*/
export async function isRLSEnabled(supabase, tableName) {
const query = `
SELECT relrowsecurity
FROM pg_class
WHERE relname = '${tableName}';
`;
const { data, error } = await supabase.rpc('exec_sql', { query });
if (error || !data || data.length === 0) {
return false;
}
return data[0].relrowsecurity === true;
}
//# sourceMappingURL=rls-queries.js.map