@intentsolutionsio/supabase-pack 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +73 -47
  3. package/package.json +4 -4
  4. package/skills/supabase-advanced-troubleshooting/SKILL.md +404 -200
  5. package/skills/supabase-advanced-troubleshooting/references/errors.md +11 -0
  6. package/skills/supabase-advanced-troubleshooting/references/evidence-collection-framework.md +34 -0
  7. package/skills/supabase-advanced-troubleshooting/references/examples.md +11 -0
  8. package/skills/supabase-advanced-troubleshooting/references/rls-edge-functions-realtime.md +363 -0
  9. package/skills/supabase-advanced-troubleshooting/references/systematic-isolation.md +56 -0
  10. package/skills/supabase-advanced-troubleshooting/references/timing-analysis.md +35 -0
  11. package/skills/supabase-architecture-variants/SKILL.md +395 -216
  12. package/skills/supabase-architecture-variants/references/errors.md +11 -0
  13. package/skills/supabase-architecture-variants/references/examples.md +12 -0
  14. package/skills/supabase-architecture-variants/references/serverless-and-multi-tenant.md +251 -0
  15. package/skills/supabase-architecture-variants/references/variant-a-monolith-(simple).md +44 -0
  16. package/skills/supabase-architecture-variants/references/variant-b-service-layer-(moderate).md +72 -0
  17. package/skills/supabase-architecture-variants/references/variant-c-microservice-(complex).md +81 -0
  18. package/skills/supabase-auth-storage-realtime-core/SKILL.md +471 -37
  19. package/skills/supabase-ci-integration/SKILL.md +315 -67
  20. package/skills/supabase-ci-integration/references/errors.md +10 -0
  21. package/skills/supabase-ci-integration/references/examples.md +36 -0
  22. package/skills/supabase-ci-integration/references/implementation.md +54 -0
  23. package/skills/supabase-common-errors/SKILL.md +320 -62
  24. package/skills/supabase-common-errors/references/errors.md +53 -0
  25. package/skills/supabase-common-errors/references/examples.md +23 -0
  26. package/skills/supabase-cost-tuning/SKILL.md +365 -131
  27. package/skills/supabase-cost-tuning/references/cost-estimation.md +34 -0
  28. package/skills/supabase-cost-tuning/references/cost-reduction-strategies.md +40 -0
  29. package/skills/supabase-cost-tuning/references/errors.md +11 -0
  30. package/skills/supabase-cost-tuning/references/examples.md +15 -0
  31. package/skills/supabase-data-handling/SKILL.md +378 -145
  32. package/skills/supabase-data-handling/references/errors.md +11 -0
  33. package/skills/supabase-data-handling/references/examples.md +27 -0
  34. package/skills/supabase-data-handling/references/implementation.md +223 -0
  35. package/skills/supabase-data-handling/references/retention-and-backup.md +221 -0
  36. package/skills/supabase-debug-bundle/SKILL.md +267 -73
  37. package/skills/supabase-debug-bundle/references/errors.md +12 -0
  38. package/skills/supabase-debug-bundle/references/examples.md +24 -0
  39. package/skills/supabase-debug-bundle/references/implementation.md +54 -0
  40. package/skills/supabase-deploy-integration/SKILL.md +258 -147
  41. package/skills/supabase-deploy-integration/references/errors.md +11 -0
  42. package/skills/supabase-deploy-integration/references/examples.md +21 -0
  43. package/skills/supabase-deploy-integration/references/google-cloud-run.md +36 -0
  44. package/skills/supabase-deploy-integration/references/vercel-deployment.md +35 -0
  45. package/skills/supabase-enterprise-rbac/SKILL.md +327 -160
  46. package/skills/supabase-enterprise-rbac/references/api-scoping-and-enforcement.md +255 -0
  47. package/skills/supabase-enterprise-rbac/references/errors.md +11 -0
  48. package/skills/supabase-enterprise-rbac/references/examples.md +12 -0
  49. package/skills/supabase-enterprise-rbac/references/role-implementation.md +33 -0
  50. package/skills/supabase-enterprise-rbac/references/sso-integration.md +35 -0
  51. package/skills/supabase-hello-world/SKILL.md +160 -54
  52. package/skills/supabase-incident-runbook/SKILL.md +453 -131
  53. package/skills/supabase-incident-runbook/references/errors.md +11 -0
  54. package/skills/supabase-incident-runbook/references/examples.md +10 -0
  55. package/skills/supabase-incident-runbook/references/immediate-actions-by-error-type.md +41 -0
  56. package/skills/supabase-install-auth/SKILL.md +186 -50
  57. package/skills/supabase-install-auth/references/examples.md +102 -0
  58. package/skills/supabase-known-pitfalls/SKILL.md +411 -241
  59. package/skills/supabase-known-pitfalls/references/errors.md +11 -0
  60. package/skills/supabase-known-pitfalls/references/examples.md +12 -0
  61. package/skills/supabase-load-scale/SKILL.md +346 -217
  62. package/skills/supabase-load-scale/references/capacity-planning.md +47 -0
  63. package/skills/supabase-load-scale/references/errors.md +11 -0
  64. package/skills/supabase-load-scale/references/examples.md +26 -0
  65. package/skills/supabase-load-scale/references/load-testing-with-k6.md +59 -0
  66. package/skills/supabase-load-scale/references/scaling-patterns.md +65 -0
  67. package/skills/supabase-load-scale/references/table-partitioning.md +263 -0
  68. package/skills/supabase-local-dev-loop/SKILL.md +272 -73
  69. package/skills/supabase-local-dev-loop/references/errors.md +11 -0
  70. package/skills/supabase-local-dev-loop/references/examples.md +21 -0
  71. package/skills/supabase-local-dev-loop/references/implementation.md +60 -0
  72. package/skills/supabase-migration-deep-dive/SKILL.md +338 -177
  73. package/skills/supabase-migration-deep-dive/references/backfill-versioning-rollback.md +258 -0
  74. package/skills/supabase-migration-deep-dive/references/errors.md +11 -0
  75. package/skills/supabase-migration-deep-dive/references/examples.md +12 -0
  76. package/skills/supabase-migration-deep-dive/references/implementation-plan.md +80 -0
  77. package/skills/supabase-migration-deep-dive/references/pre-migration-assessment.md +39 -0
  78. package/skills/supabase-multi-env-setup/SKILL.md +393 -152
  79. package/skills/supabase-multi-env-setup/references/configuration-structure.md +59 -0
  80. package/skills/supabase-multi-env-setup/references/errors.md +11 -0
  81. package/skills/supabase-multi-env-setup/references/examples.md +11 -0
  82. package/skills/supabase-observability/SKILL.md +318 -196
  83. package/skills/supabase-observability/references/alert-configuration.md +40 -0
  84. package/skills/supabase-observability/references/errors.md +11 -0
  85. package/skills/supabase-observability/references/examples.md +13 -0
  86. package/skills/supabase-observability/references/metrics-collection.md +65 -0
  87. package/skills/supabase-performance-tuning/SKILL.md +304 -160
  88. package/skills/supabase-performance-tuning/references/caching-strategy.md +49 -0
  89. package/skills/supabase-performance-tuning/references/errors.md +11 -0
  90. package/skills/supabase-performance-tuning/references/examples.md +13 -0
  91. package/skills/supabase-policy-guardrails/SKILL.md +248 -221
  92. package/skills/supabase-policy-guardrails/references/ci-cost-security.md +484 -0
  93. package/skills/supabase-policy-guardrails/references/errors.md +11 -0
  94. package/skills/supabase-policy-guardrails/references/eslint-rules.md +46 -0
  95. package/skills/supabase-policy-guardrails/references/examples.md +10 -0
  96. package/skills/supabase-prod-checklist/SKILL.md +474 -84
  97. package/skills/supabase-prod-checklist/references/errors.md +63 -0
  98. package/skills/supabase-prod-checklist/references/examples.md +153 -0
  99. package/skills/supabase-prod-checklist/references/implementation.md +113 -0
  100. package/skills/supabase-rate-limits/SKILL.md +311 -98
  101. package/skills/supabase-rate-limits/references/errors.md +11 -0
  102. package/skills/supabase-rate-limits/references/examples.md +46 -0
  103. package/skills/supabase-rate-limits/references/implementation.md +66 -0
  104. package/skills/supabase-reference-architecture/SKILL.md +249 -182
  105. package/skills/supabase-reference-architecture/references/errors.md +29 -0
  106. package/skills/supabase-reference-architecture/references/examples.md +116 -0
  107. package/skills/supabase-reference-architecture/references/key-components.md +244 -0
  108. package/skills/supabase-reference-architecture/references/project-structure.md +109 -0
  109. package/skills/supabase-reliability-patterns/SKILL.md +229 -234
  110. package/skills/supabase-reliability-patterns/references/circuit-breaker.md +36 -0
  111. package/skills/supabase-reliability-patterns/references/dead-letter-queue.md +48 -0
  112. package/skills/supabase-reliability-patterns/references/errors.md +11 -0
  113. package/skills/supabase-reliability-patterns/references/examples.md +11 -0
  114. package/skills/supabase-reliability-patterns/references/idempotency-keys.md +36 -0
  115. package/skills/supabase-reliability-patterns/references/offline-degradation-health-dualwrite.md +489 -0
  116. package/skills/supabase-schema-from-requirements/SKILL.md +373 -34
  117. package/skills/supabase-sdk-patterns/SKILL.md +388 -99
  118. package/skills/supabase-sdk-patterns/references/errors.md +11 -0
  119. package/skills/supabase-sdk-patterns/references/examples.md +45 -0
  120. package/skills/supabase-sdk-patterns/references/implementation.md +67 -0
  121. package/skills/supabase-security-basics/SKILL.md +282 -102
  122. package/skills/supabase-security-basics/references/errors.md +10 -0
  123. package/skills/supabase-security-basics/references/examples.md +70 -0
  124. package/skills/supabase-security-basics/references/implementation.md +39 -0
  125. package/skills/supabase-upgrade-migration/SKILL.md +248 -66
  126. package/skills/supabase-upgrade-migration/references/errors.md +10 -0
  127. package/skills/supabase-upgrade-migration/references/examples.md +51 -0
  128. package/skills/supabase-upgrade-migration/references/implementation.md +29 -0
  129. package/skills/supabase-webhooks-events/SKILL.md +412 -138
  130. package/skills/supabase-webhooks-events/references/errors.md +55 -0
  131. package/skills/supabase-webhooks-events/references/event-handler-pattern.md +106 -0
  132. package/skills/supabase-webhooks-events/references/examples.md +133 -0
  133. package/skills/supabase-webhooks-events/references/signature-verification.md +165 -0
@@ -0,0 +1,484 @@
1
+ ## Migration Review, Cost Alerts, and Security Audit
2
+
3
+ ### GitHub Actions Migration Guardrails
4
+
5
+ ```yaml
6
+ # .github/workflows/supabase-guardrails.yml
7
+ name: Supabase Migration Guardrails
8
+
9
+ on:
10
+ pull_request:
11
+ paths:
12
+ - 'supabase/migrations/**'
13
+
14
+ jobs:
15
+ migration-review:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: supabase/setup-cli@v1
20
+
21
+ - name: Start local Supabase
22
+ run: supabase start
23
+
24
+ - name: Apply migrations
25
+ run: supabase db reset
26
+
27
+ - name: Check RLS enabled on all public tables
28
+ run: |
29
+ MISSING_RLS=$(supabase db query "
30
+ SELECT tablename FROM pg_tables
31
+ WHERE schemaname = 'public'
32
+ AND rowsecurity = false
33
+ AND tablename NOT LIKE '\_%'
34
+ AND tablename NOT IN ('schema_migrations')
35
+ " --output csv | tail -n +2)
36
+
37
+ if [ -n "$MISSING_RLS" ]; then
38
+ echo "::error::Tables missing RLS: $MISSING_RLS"
39
+ echo "Fix: ALTER TABLE public.<table> ENABLE ROW LEVEL SECURITY;"
40
+ exit 1
41
+ fi
42
+ echo "All public tables have RLS enabled"
43
+
44
+ - name: Check migration naming convention
45
+ run: |
46
+ for file in supabase/migrations/*.sql; do
47
+ basename=$(basename "$file")
48
+ if ! echo "$basename" | grep -qE '^[0-9]{14}_(create|alter|drop|add|remove|update|fix|seed|enable|disable)_[a-z_]+\.sql$'; then
49
+ echo "::error::Migration '$basename' violates naming convention"
50
+ echo "Expected: <14-digit-timestamp>_<verb>_<description>.sql"
51
+ exit 1
52
+ fi
53
+ done
54
+ echo "Migration naming convention check passed"
55
+
56
+ - name: Block unannotated destructive operations
57
+ run: |
58
+ for file in supabase/migrations/*.sql; do
59
+ if grep -qiE 'DROP TABLE|DROP COLUMN|TRUNCATE|DELETE FROM.*WHERE\s+(1=1|true)' "$file"; then
60
+ if ! grep -qi '-- APPROVED-DESTRUCTIVE:' "$file"; then
61
+ echo "::error::Destructive operation in $file without approval annotation"
62
+ echo "Add '-- APPROVED-DESTRUCTIVE: <reason>' to acknowledge"
63
+ exit 1
64
+ fi
65
+ fi
66
+ done
67
+ echo "Destructive operation check passed"
68
+
69
+ - name: Validate naming conventions
70
+ run: |
71
+ ISSUES=$(supabase db query "SELECT * FROM public.validate_naming_conventions()" --output csv | tail -n +2)
72
+ if [ -n "$ISSUES" ]; then
73
+ echo "::warning::Naming convention issues found:"
74
+ echo "$ISSUES"
75
+ fi
76
+
77
+ - name: Check foreign key indexes
78
+ run: |
79
+ MISSING_INDEXES=$(supabase db query "
80
+ SELECT
81
+ tc.table_name,
82
+ kcu.column_name
83
+ FROM information_schema.table_constraints tc
84
+ JOIN information_schema.key_column_usage kcu
85
+ ON tc.constraint_name = kcu.constraint_name
86
+ LEFT JOIN pg_indexes pi
87
+ ON pi.tablename = tc.table_name
88
+ AND pi.indexdef LIKE '%' || kcu.column_name || '%'
89
+ WHERE tc.constraint_type = 'FOREIGN KEY'
90
+ AND tc.table_schema = 'public'
91
+ AND pi.indexname IS NULL
92
+ " --output csv | tail -n +2)
93
+
94
+ if [ -n "$MISSING_INDEXES" ]; then
95
+ echo "::warning::Foreign key columns missing indexes: $MISSING_INDEXES"
96
+ fi
97
+
98
+ - name: Stop Supabase
99
+ if: always()
100
+ run: supabase stop
101
+ ```
102
+
103
+ ### Pre-Commit Hook for Secrets and SQL Lint
104
+
105
+ ```bash
106
+ #!/bin/bash
107
+ # scripts/supabase-pre-commit.sh
108
+ set -euo pipefail
109
+
110
+ echo "Running Supabase pre-commit checks..."
111
+
112
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
113
+
114
+ # Check 1: No hardcoded Supabase keys (JWT format)
115
+ if echo "$STAGED_FILES" | grep -v '.env' | grep -v 'pnpm-lock' | \
116
+ xargs grep -lE 'eyJ[A-Za-z0-9_-]{50,}\.' 2>/dev/null; then
117
+ echo "ERROR: Possible Supabase API key in staged files"
118
+ echo "Use environment variables instead"
119
+ exit 1
120
+ fi
121
+
122
+ # Check 2: No connection strings
123
+ if echo "$STAGED_FILES" | xargs grep -lE 'postgres://postgres\.[a-z]+:' 2>/dev/null; then
124
+ echo "ERROR: Supabase connection string in staged files"
125
+ exit 1
126
+ fi
127
+
128
+ # Check 3: Migration files have RLS (new tables)
129
+ for file in $(echo "$STAGED_FILES" | grep 'supabase/migrations/.*\.sql$' || true); do
130
+ if grep -qi 'CREATE TABLE public\.' "$file"; then
131
+ if ! grep -qi 'ENABLE ROW LEVEL SECURITY' "$file"; then
132
+ echo "ERROR: $file creates a table without enabling RLS"
133
+ echo "Add: ALTER TABLE public.<table> ENABLE ROW LEVEL SECURITY;"
134
+ exit 1
135
+ fi
136
+ fi
137
+ done
138
+
139
+ echo "Supabase pre-commit checks passed"
140
+ ```
141
+
142
+ ```bash
143
+ # Install with Husky
144
+ npx husky add .husky/pre-commit 'bash scripts/supabase-pre-commit.sh'
145
+ ```
146
+
147
+ ## Step 3 — Cost Alerts and Security Audit Scripts
148
+
149
+ ### Cost Alert Configuration
150
+
151
+ ```typescript
152
+ // scripts/supabase-cost-monitor.ts
153
+ import { createClient } from '@supabase/supabase-js'
154
+
155
+ // Use the Supabase Management API for cost monitoring
156
+ const SUPABASE_ACCESS_TOKEN = process.env.SUPABASE_ACCESS_TOKEN!
157
+ const PROJECT_REF = process.env.SUPABASE_PROJECT_REF!
158
+
159
+ interface UsageMetrics {
160
+ database_size_gb: number
161
+ storage_size_gb: number
162
+ bandwidth_gb: number
163
+ edge_function_invocations: number
164
+ monthly_active_users: number
165
+ }
166
+
167
+ // Cost thresholds — adjust per your budget
168
+ const THRESHOLDS = {
169
+ database_size_gb: 8, // Pro includes 8 GB
170
+ storage_size_gb: 100, // Pro includes 100 GB
171
+ bandwidth_gb: 250, // Pro includes 250 GB
172
+ edge_function_invocations: 2_000_000, // Pro includes 2M
173
+ monthly_active_users: 100_000, // Pro limit
174
+ }
175
+
176
+ async function checkCostAlerts() {
177
+ // Fetch current usage via Supabase Management API
178
+ const response = await fetch(
179
+ `https://api.supabase.com/v1/projects/${PROJECT_REF}/usage`,
180
+ {
181
+ headers: { Authorization: `Bearer ${SUPABASE_ACCESS_TOKEN}` },
182
+ }
183
+ )
184
+
185
+ if (!response.ok) {
186
+ console.error('Failed to fetch usage:', response.statusText)
187
+ return
188
+ }
189
+
190
+ const usage: UsageMetrics = await response.json()
191
+
192
+ const alerts: string[] = []
193
+
194
+ for (const [metric, threshold] of Object.entries(THRESHOLDS)) {
195
+ const current = usage[metric as keyof UsageMetrics] as number
196
+ const percent = (current / threshold) * 100
197
+
198
+ if (percent >= 90) {
199
+ alerts.push(`CRITICAL: ${metric} at ${percent.toFixed(1)}% (${current}/${threshold})`)
200
+ } else if (percent >= 75) {
201
+ alerts.push(`WARNING: ${metric} at ${percent.toFixed(1)}% (${current}/${threshold})`)
202
+ }
203
+ }
204
+
205
+ if (alerts.length > 0) {
206
+ console.warn('Cost alerts:\n' + alerts.join('\n'))
207
+ // Send to Slack, PagerDuty, email, etc.
208
+ await sendCostAlert(alerts)
209
+ } else {
210
+ console.log('All usage metrics within budget')
211
+ }
212
+ }
213
+
214
+ async function sendCostAlert(alerts: string[]) {
215
+ // Example: Slack webhook
216
+ const webhookUrl = process.env.SLACK_WEBHOOK_URL
217
+ if (!webhookUrl) return
218
+
219
+ await fetch(webhookUrl, {
220
+ method: 'POST',
221
+ headers: { 'Content-Type': 'application/json' },
222
+ body: JSON.stringify({
223
+ text: `*Supabase Cost Alert* (${PROJECT_REF})\n${alerts.join('\n')}`,
224
+ }),
225
+ })
226
+ }
227
+ ```
228
+
229
+ ### Security Audit Script
230
+
231
+ ```typescript
232
+ // scripts/supabase-security-audit.ts
233
+ import { createClient } from '@supabase/supabase-js'
234
+
235
+ const supabase = createClient(
236
+ process.env.SUPABASE_URL!,
237
+ process.env.SUPABASE_SERVICE_ROLE_KEY!,
238
+ { auth: { autoRefreshToken: false, persistSession: false } }
239
+ )
240
+
241
+ interface AuditFinding {
242
+ severity: 'critical' | 'high' | 'medium' | 'low'
243
+ category: string
244
+ description: string
245
+ remediation: string
246
+ }
247
+
248
+ export async function runSecurityAudit(): Promise<AuditFinding[]> {
249
+ const findings: AuditFinding[] = []
250
+
251
+ // Check 1: Tables without RLS
252
+ const { data: noRls } = await supabase.rpc('run_sql', {
253
+ sql: `
254
+ SELECT tablename FROM pg_tables
255
+ WHERE schemaname = 'public'
256
+ AND rowsecurity = false
257
+ AND tablename NOT LIKE '\\_%'
258
+ `,
259
+ })
260
+
261
+ for (const row of noRls ?? []) {
262
+ findings.push({
263
+ severity: 'critical',
264
+ category: 'RLS',
265
+ description: `Table "${row.tablename}" has RLS disabled`,
266
+ remediation: `ALTER TABLE public.${row.tablename} ENABLE ROW LEVEL SECURITY;`,
267
+ })
268
+ }
269
+
270
+ // Check 2: Tables with RLS enabled but no policies
271
+ const { data: noPolicies } = await supabase.rpc('run_sql', {
272
+ sql: `
273
+ SELECT t.tablename
274
+ FROM pg_tables t
275
+ LEFT JOIN pg_policies p ON p.tablename = t.tablename AND p.schemaname = t.schemaname
276
+ WHERE t.schemaname = 'public'
277
+ AND t.rowsecurity = true
278
+ AND p.policyname IS NULL
279
+ `,
280
+ })
281
+
282
+ for (const row of noPolicies ?? []) {
283
+ findings.push({
284
+ severity: 'high',
285
+ category: 'RLS',
286
+ description: `Table "${row.tablename}" has RLS enabled but no policies (blocks all access)`,
287
+ remediation: 'Add appropriate RLS policies or this table is inaccessible via API',
288
+ })
289
+ }
290
+
291
+ // Check 3: Overly permissive policies (USING (true) for non-public tables)
292
+ const { data: permissive } = await supabase.rpc('run_sql', {
293
+ sql: `
294
+ SELECT tablename, policyname, qual
295
+ FROM pg_policies
296
+ WHERE schemaname = 'public'
297
+ AND qual = 'true'
298
+ AND cmd != 'SELECT'
299
+ `,
300
+ })
301
+
302
+ for (const row of permissive ?? []) {
303
+ findings.push({
304
+ severity: 'high',
305
+ category: 'RLS',
306
+ description: `Policy "${row.policyname}" on "${row.tablename}" allows unrestricted writes (USING true)`,
307
+ remediation: 'Restrict policy to owner or organization scope',
308
+ })
309
+ }
310
+
311
+ // Check 4: Foreign key columns without indexes
312
+ const { data: missingIdx } = await supabase.rpc('run_sql', {
313
+ sql: `
314
+ SELECT
315
+ tc.table_name,
316
+ kcu.column_name
317
+ FROM information_schema.table_constraints tc
318
+ JOIN information_schema.key_column_usage kcu
319
+ ON tc.constraint_name = kcu.constraint_name
320
+ LEFT JOIN pg_indexes pi
321
+ ON pi.tablename = tc.table_name
322
+ AND pi.indexdef LIKE '%' || kcu.column_name || '%'
323
+ WHERE tc.constraint_type = 'FOREIGN KEY'
324
+ AND tc.table_schema = 'public'
325
+ AND pi.indexname IS NULL
326
+ `,
327
+ })
328
+
329
+ for (const row of missingIdx ?? []) {
330
+ findings.push({
331
+ severity: 'medium',
332
+ category: 'Performance',
333
+ description: `Foreign key ${row.table_name}.${row.column_name} has no index`,
334
+ remediation: `CREATE INDEX idx_${row.table_name}_${row.column_name} ON public.${row.table_name}(${row.column_name});`,
335
+ })
336
+ }
337
+
338
+ // Check 5: Storage buckets without RLS
339
+ const { data: buckets } = await supabase.storage.listBuckets()
340
+ for (const bucket of buckets ?? []) {
341
+ if (bucket.public) {
342
+ findings.push({
343
+ severity: 'low',
344
+ category: 'Storage',
345
+ description: `Bucket "${bucket.name}" is public — verify this is intentional`,
346
+ remediation: 'Set bucket to private if it contains sensitive files',
347
+ })
348
+ }
349
+ }
350
+
351
+ return findings
352
+ }
353
+
354
+ // Run and display results
355
+ async function main() {
356
+ const findings = await runSecurityAudit()
357
+
358
+ const critical = findings.filter(f => f.severity === 'critical')
359
+ const high = findings.filter(f => f.severity === 'high')
360
+
361
+ console.log(`\nSecurity Audit Results:`)
362
+ console.log(` Critical: ${critical.length}`)
363
+ console.log(` High: ${high.length}`)
364
+ console.log(` Medium: ${findings.filter(f => f.severity === 'medium').length}`)
365
+ console.log(` Low: ${findings.filter(f => f.severity === 'low').length}`)
366
+
367
+ for (const finding of findings) {
368
+ console.log(`\n[${finding.severity.toUpperCase()}] ${finding.category}: ${finding.description}`)
369
+ console.log(` Fix: ${finding.remediation}`)
370
+ }
371
+
372
+ // Exit with error code if critical/high issues found
373
+ if (critical.length > 0 || high.length > 0) {
374
+ process.exit(1)
375
+ }
376
+ }
377
+
378
+ main()
379
+ ```
380
+
381
+ ### Scheduled Audit via Edge Function
382
+
383
+ ```typescript
384
+ // supabase/functions/security-audit/index.ts
385
+ import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
386
+
387
+ Deno.serve(async () => {
388
+ const supabase = createClient(
389
+ Deno.env.get('SUPABASE_URL')!,
390
+ Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
391
+ )
392
+
393
+ // Check tables without RLS
394
+ const { data: noRls } = await supabase
395
+ .from('pg_tables')
396
+ .select('tablename')
397
+ .eq('schemaname', 'public')
398
+ .eq('rowsecurity', false)
399
+
400
+ const issues = (noRls ?? []).map(t => t.tablename)
401
+
402
+ if (issues.length > 0) {
403
+ // Store audit result
404
+ await supabase.from('audit_log').insert({
405
+ event: 'security_audit',
406
+ severity: 'critical',
407
+ details: { tables_without_rls: issues },
408
+ })
409
+ }
410
+
411
+ return new Response(JSON.stringify({
412
+ status: issues.length === 0 ? 'pass' : 'fail',
413
+ tables_without_rls: issues,
414
+ checked_at: new Date().toISOString(),
415
+ }))
416
+ })
417
+ ```
418
+
419
+ ## Output
420
+
421
+ - Shared RLS policy library with owner-only, org-scoped, and public-read templates
422
+ - Naming convention validation function checking tables, columns, FKs, and booleans
423
+ - CI pipeline enforcing RLS, naming, and destructive operation controls
424
+ - Pre-commit hook blocking hardcoded secrets and tables without RLS
425
+ - Cost monitoring script with configurable thresholds and Slack alerting
426
+ - Security audit script detecting missing RLS, permissive policies, and missing indexes
427
+ - Scheduled Edge Function for continuous security monitoring
428
+
429
+ ## Error Handling
430
+
431
+ | Issue | Cause | Solution |
432
+ |-------|-------|----------|
433
+ | CI RLS check fails on new table | Migration missing `ENABLE ROW LEVEL SECURITY` | Add `ALTER TABLE` after `CREATE TABLE` in same migration |
434
+ | Naming convention false positive | Table is intentionally singular (e.g., `config`) | Add to exclusion list in validation function |
435
+ | Cost alert not firing | Missing `SUPABASE_ACCESS_TOKEN` | Generate token at supabase.com/dashboard/account/tokens |
436
+ | Security audit times out | Too many tables to scan | Run audit on specific schemas or paginate results |
437
+ | Pre-commit blocks legitimate JWT in test | Test fixture contains JWT-like string | Add test file path to exclusion pattern |
438
+ | RLS template function not found | Migration not applied | Run `supabase db reset` or apply migration manually |
439
+
440
+ ## Examples
441
+
442
+ ### Apply RLS Template to a New Table
443
+
444
+ ```sql
445
+ -- Create the table
446
+ CREATE TABLE public.tasks (
447
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
448
+ org_id uuid NOT NULL REFERENCES public.organizations(id),
449
+ title text NOT NULL,
450
+ is_complete boolean DEFAULT false,
451
+ created_by uuid REFERENCES auth.users(id),
452
+ created_at timestamptz DEFAULT now()
453
+ );
454
+
455
+ -- Apply org-scoped RLS template (with delete for admins)
456
+ SELECT public.rls_org_scoped('tasks', 'org_id', true);
457
+
458
+ -- Create index on foreign key
459
+ CREATE INDEX idx_tasks_org_id ON public.tasks(org_id);
460
+ ```
461
+
462
+ ### Run Security Audit Locally
463
+
464
+ ```bash
465
+ npx tsx scripts/supabase-security-audit.ts
466
+ ```
467
+
468
+ ### Check Naming Conventions
469
+
470
+ ```sql
471
+ SELECT * FROM public.validate_naming_conventions();
472
+ ```
473
+
474
+ ## Resources
475
+
476
+ - [Supabase Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security)
477
+ - [Supabase CLI Migrations](https://supabase.com/docs/guides/cli/managing-environments)
478
+ - [Supabase Management API](https://supabase.com/docs/reference/api/introduction)
479
+ - [Supabase Pricing](https://supabase.com/pricing)
480
+ - [PostgreSQL Naming Conventions](https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS)
481
+
482
+ ## Next Steps
483
+
484
+ For architecture patterns across different app types, see `supabase-architecture-variants`.
@@ -0,0 +1,11 @@
1
+ # Error Handling Reference
2
+
3
+ | Issue | Cause | Solution |
4
+ |-------|-------|----------|
5
+ | ESLint rule not firing | Wrong config | Check plugin registration |
6
+ | Pre-commit skipped | --no-verify | Enforce in CI |
7
+ | Policy false positive | Regex too broad | Narrow pattern match |
8
+ | Guardrail triggered | Actual issue | Fix or whitelist |
9
+
10
+ ---
11
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
@@ -0,0 +1,46 @@
1
+ # Eslint Rules
2
+
3
+ ## ESLint Rules
4
+
5
+ ### Custom Supabase Plugin
6
+
7
+ ```javascript
8
+ // eslint-plugin-supabase/rules/no-hardcoded-keys.js
9
+ module.exports = {
10
+ meta: {
11
+ type: 'problem',
12
+ docs: {
13
+ description: 'Disallow hardcoded Supabase API keys',
14
+ },
15
+ fixable: 'code',
16
+ },
17
+ create(context) {
18
+ return {
19
+ Literal(node) {
20
+ if (typeof node.value === 'string') {
21
+ if (node.value.match(/^sk_(live|test)_[a-zA-Z0-9]{24,}/)) {
22
+ context.report({
23
+ node,
24
+ message: 'Hardcoded Supabase API key detected',
25
+ });
26
+ }
27
+ }
28
+ },
29
+ };
30
+ },
31
+ };
32
+ ```
33
+
34
+ ### ESLint Configuration
35
+
36
+ ```javascript
37
+ // .eslintrc.js
38
+ module.exports = {
39
+ plugins: ['supabase'],
40
+ rules: {
41
+ 'supabase/no-hardcoded-keys': 'error',
42
+ 'supabase/require-error-handling': 'warn',
43
+ 'supabase/use-typed-client': 'warn',
44
+ },
45
+ };
46
+ ```
@@ -0,0 +1,10 @@
1
+ ## Examples
2
+
3
+ ### Quick ESLint Check
4
+
5
+ ```bash
6
+ npx eslint --plugin supabase --rule 'supabase/no-hardcoded-keys: error' src/
7
+ ```
8
+
9
+ ---
10
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*