ワンクリックで
db-policy-apply
Install KISS or granular RLS policies on a table
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Install KISS or granular RLS policies on a table
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Execute story development with selectable automation modes to accommodate different developer preferences, skill levels, and story complexity.
Set up a Kord-aligned documentation baseline (no legacy frameworks)
Create Next Story Task methodology and workflow
Validate Next Story Task methodology and workflow
advanced-elicitation methodology and workflow
No checklists needed - this task facilitates brainstorming sessions, validation is through user interaction methodology and workflow
| name | db-policy-apply |
| description | Install KISS or granular RLS policies on a table |
| agent | architect |
| subtask | false |
Install KISS or granular RLS policies on a table
Parameter:* mode (optional, default: interactive)
Purpose:* Definitive pass/fail criteria for task completion
Checklist:*
acceptance-criteria:
- [ ] Data persisted correctly; constraints respected; no orphaned data
type: acceptance-criterion
blocker: true
validation: |
Assert data persisted correctly; constraints respected; no orphaned data
error_message: "Acceptance criterion not met: Data persisted correctly; constraints respected; no orphaned data"
Strategy:* abort
Common Errors:*
Error:* Connection Failed
Error:* Query Syntax Error
Error:* Transaction Rollback
Token Savings: 89% | Time Savings: ~85%*
# Use the rls-policy-installer script
./Squads/super-agentes/scripts/database-operations/rls-policy-installer.sh {table} {mode}
# Examples:
./Squads/super-agentes/scripts/database-operations/rls-policy-installer.sh minds kiss
./Squads/super-agentes/scripts/database-operations/rls-policy-installer.sh sources read-only
./Squads/super-agentes/scripts/database-operations/rls-policy-installer.sh fragments private
# Available modes: kiss, read-only, private, team, custom
# Benefits:
# - Standardized policy templates
# - Automatic testing after installation
# - Safety checks for existing policies
# - 89% token savings
OR continue with manual policy installation below:*
table (string): Table name to apply policy tomode (string): 'kiss' or 'granular' - policy typeCheck table exists and mode is valid:
echo "Validating inputs..."
# Check table exists
psql "$SUPABASE_DB_URL" -c \
"SELECT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = '{table}'
);" | grep -q t || {
echo "❌ Table '{table}' not found"
exit 1
}
# Check mode
if [[ "{mode}" != "kiss" && "{mode}" != "granular" ]]; then
echo "❌ Invalid mode: {mode}"
echo " Use 'kiss' or 'granular'"
exit 1
fi
echo "✓ Table exists: {table}"
echo "✓ Mode: {mode}"
Display current RLS status:
echo "Checking existing RLS policies..."
psql "$SUPABASE_DB_URL" << EOF
SELECT
schemaname,
tablename,
policyname,
permissive,
roles,
cmd,
qual,
with_check
FROM pg_policies
WHERE tablename = '{table}';
EOF
echo ""
echo "RLS enabled on {table}?"
psql "$SUPABASE_DB_URL" -c \
"SELECT relrowsecurity FROM pg_class WHERE relname = '{table}';" \
| grep -q t && echo "✓ Yes" || echo "⚠️ No (will be enabled)"
Present policy that will be applied based on mode:
If mode = 'kiss':*
Will apply KISS policy to {table}:
- Enable RLS
- Single policy: users can only access their own rows
- Uses: (select auth.uid()) = user_id [PERFORMANCE OPTIMIZED]
- Applies to: SELECT, INSERT, UPDATE, DELETE
⚠️ CRITICAL PERFORMANCE NOTE:
Wrapping auth.uid() in SELECT provides 99.99% performance improvement
by allowing PostgreSQL to cache the function result.
Continue? (yes/no)
If mode = 'granular':*
Will apply granular policies to {table}:
- Enable RLS
- Separate policies for each operation (SELECT, INSERT, UPDATE, DELETE)
- Fine-grained control
- Uses: auth.uid() = user_id
Continue? (yes/no)
Get confirmation before proceeding.
Based on mode, generate appropriate SQL:
KISS Mode:*
-- Enable RLS
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
-- Drop existing policies (if any)
DROP POLICY IF EXISTS "{table}_policy" ON {table};
-- Create single KISS policy (PERFORMANCE OPTIMIZED)
CREATE POLICY "{table}_policy"
ON {table}
FOR ALL
TO authenticated
USING (
-- ✅ CRITICAL: Wrap auth.uid() in SELECT for 99.99% performance gain
-- This allows PostgreSQL to cache the function result per statement
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
)
WITH CHECK (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- Add helpful comment
COMMENT ON POLICY "{table}_policy" ON {table} IS
'KISS policy: users can only access their own rows (performance optimized with cached auth.uid())';
Granular Mode (PERFORMANCE OPTIMIZED):*
-- Enable RLS
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
-- Drop existing policies (if any)
DROP POLICY IF EXISTS "{table}_select" ON {table};
DROP POLICY IF EXISTS "{table}_insert" ON {table};
DROP POLICY IF EXISTS "{table}_update" ON {table};
DROP POLICY IF EXISTS "{table}_delete" ON {table};
-- SELECT: Users read own rows
-- ✅ Wrapping auth.uid() in SELECT provides 99.99% performance improvement
CREATE POLICY "{table}_select"
ON {table}
FOR SELECT
TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- INSERT: Users create own rows
CREATE POLICY "{table}_insert"
ON {table}
FOR INSERT
TO authenticated
WITH CHECK (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- UPDATE: Users update own rows
CREATE POLICY "{table}_update"
ON {table}
FOR UPDATE
TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
)
WITH CHECK (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- DELETE: Users delete own rows
CREATE POLICY "{table}_delete"
ON {table}
FOR DELETE
TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- Add helpful comments
COMMENT ON POLICY "{table}_select" ON {table} IS 'Users can read own rows (cached auth.uid())';
COMMENT ON POLICY "{table}_insert" ON {table} IS 'Users can insert own rows (cached auth.uid())';
COMMENT ON POLICY "{table}_update" ON {table} IS 'Users can update own rows (cached auth.uid())';
COMMENT ON POLICY "{table}_delete" ON {table} IS 'Users can delete own rows (cached auth.uid())';
Save policy SQL to migration file:
TS=$(date +%Y%m%d%H%M%S)
MIGRATION_FILE="supabase/migrations/${TS}_rls_${mode}__{table}.sql"
mkdir -p supabase/migrations
cat > "$MIGRATION_FILE" << 'EOF'
-- Migration: Apply {mode} RLS policy to {table}
-- Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
-- Table: {table}
-- Mode: {mode}
BEGIN;
[... SQL from step 4 ...]
COMMIT;
EOF
echo "✓ Migration created: $MIGRATION_FILE"
Use existing db-apply-migration task:
echo "Applying migration..."
# Execute db-apply-migration task internally
# (This will create snapshots, apply, verify)
Verify policies work correctly:
echo "Testing RLS policies..."
# Test 1: Anonymous user should see nothing
psql "$SUPABASE_DB_URL" << EOF
SET ROLE anon;
SELECT COUNT(*) AS anon_count FROM {table};
RESET ROLE;
EOF
# Test 2: Authenticated user should see only their rows
# (Requires setting up test user - provide instructions)
echo ""
echo "✓ Policy tests complete"
echo " ⚠️ Manual testing recommended:"
echo " - Use impersonate to test as specific user"
echo " - Verify each operation (SELECT, INSERT, UPDATE, DELETE)"
Display summary:
✅ RLS POLICY APPLIED
Table: {table}
Mode: {mode}
Migration: supabase/migrations/{TS}_rls_{mode}__{table}.sql
Policies: [list created policies]
Next steps:
1. Test policies manually: impersonate {user_id}
2. Run RLS audit: rls-audit
3. Update documentation
4. Commit migration to git
KISS* (Keep It Simple, Stupid):
Granular*:
Public Read, Authenticated Write (Performance Optimized):*
-- SELECT: Public
CREATE POLICY "{table}_select" ON {table}
FOR SELECT TO public
USING (true);
-- INSERT/UPDATE/DELETE: Authenticated users only
CREATE POLICY "{table}_write" ON {table}
FOR ALL TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
)
WITH CHECK (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
Tenant-Based (Performance Optimized):*
CREATE POLICY "{table}_tenant" ON {table}
FOR ALL TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
tenant_id IN (
SELECT tenant_id FROM user_tenants
WHERE user_id = (select auth.uid())
)
);
-- ❌ DANGEROUS - User can modify this data!
CREATE POLICY "bad_policy" ON {table}
USING (
(auth.jwt() -> 'user_metadata' ->> 'role') = 'admin'
);
Why dangerous:* raw_user_meta_data can be modified by the user through Supabase Auth client. An attacker can set { "role": "admin" } and bypass security!
Safe alternative:* Use raw_app_meta_data (server-only):
-- ✅ SAFE - Only server can modify app_metadata
CREATE POLICY "safe_policy" ON {table}
USING (
(auth.jwt() -> 'app_metadata' ->> 'role') = 'admin'
);
Always check if user is authenticated:
-- ❌ Missing NULL check
USING (auth.uid() = user_id) -- Fails silently for anon users
-- ✅ Explicit authentication check
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
)
Enable RLS policies in SQL Editor (dev only):
-- Temporarily disable RLS for debugging (DANGEROUS - dev only!)
ALTER TABLE {table} DISABLE ROW LEVEL SECURITY;
-- Re-enable when done
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
Table must have:
user_id UUID column (for user-based policies)tenant_id column (for tenant-based policies)CREATE INDEX idx_{table}_user_id ON {table}(user_id);If policy application fails:
rollback