diff --git a/apps/web/src/data/eval-results.json b/apps/web/src/data/eval-results.json index 5b28a57e..8fa43a97 100644 --- a/apps/web/src/data/eval-results.json +++ b/apps/web/src/data/eval-results.json @@ -660,6 +660,103 @@ "attempts": 1, "sourcePath": "claude-code-opus-5/build-functions-005-dual-auth-user-secret.json" }, + { + "experiment": "claude-code-opus-5", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "claude-code", + "modelProvider": "anthropic", + "modelId": "claude-opus-5", + "reasoningEffort": "high" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase" + ] + }, + "docs": { + "calls": [] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "claude-code-opus-5/build-rls-003-org-roles-permissions.json" + }, { "experiment": "claude-code-opus-5", "experimentSuite": "benchmark", @@ -2705,6 +2802,98 @@ "attempts": 2, "sourcePath": "claude-code-opus-5-no-skills/build-functions-005-dual-auth-user-secret.json" }, + { + "experiment": "claude-code-opus-5-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "claude-code", + "modelProvider": "anthropic", + "modelId": "claude-opus-5", + "reasoningEffort": "high" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "claude-code-opus-5-no-skills/build-rls-003-org-roles-permissions.json" + }, { "experiment": "claude-code-opus-5-no-skills", "experimentSuite": "no-skills", @@ -4718,6 +4907,104 @@ "attempts": 1, "sourcePath": "claude-code-sonnet-5/build-functions-005-dual-auth-user-secret.json" }, + { + "experiment": "claude-code-sonnet-5", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "claude-code", + "modelProvider": "anthropic", + "modelId": "claude-sonnet-5", + "reasoningEffort": "high" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase", + "supabase-postgres-best-practices" + ] + }, + "docs": { + "calls": [] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "claude-code-sonnet-5/build-rls-003-org-roles-permissions.json" + }, { "experiment": "claude-code-sonnet-5", "experimentSuite": "benchmark", @@ -6080,59 +6367,151 @@ "modelId": "claude-sonnet-5", "reasoningEffort": "high" }, - "eval": "build-storage-001-private-bucket-access", + "eval": "build-rls-003-org-roles-permissions", "stage": "build", "product": [ - "storage", - "database" + "database", + "auth" ], "topic": [ "rls", - "sdk" + "security" ], "suite": "benchmark", "interface": "mcp", "passed": true, "checks": [ { - "name": "bucket user-files exists", + "name": "RLS enabled on documents", "passed": true }, { - "name": "bucket user-files is private", + "name": "viewer sees only documents in their org", "passed": true }, { - "name": "RLS still enabled on storage.objects", + "name": "viewer cannot insert", "passed": true }, { - "name": "user A lists only own files", - "passed": true, - "notes": "saw: 019fb0e7-0613-730a-ae13-0efd0c195137/receipt-alpha.pdf, 019fb0e7-0613-730a-ae13-0efd0c195137/receipt-beta.pdf" + "name": "editor can insert own org document", + "passed": true }, { - "name": "user B cannot read user A files", + "name": "editor can update own document", "passed": true }, { - "name": "anon reads no files", + "name": "editor cannot update another user's document", "passed": true }, { - "name": "user A can upload into own folder", + "name": "editor cannot delete another user's document", "passed": true }, { - "name": "user B cannot upload into user A folder", + "name": "admin can update any document in their org", "passed": true }, { - "name": "configured private per-user storage access", - "passed": true, - "judgeNotes": "Meets requirements: private user-files bucket, authenticated owner-scoped SELECT and INSERT policies with WITH CHECK on storage.objects, no RLS disabling or public access, and supabase-js createSignedUrl with expiry for sharing." - } - ], + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "claude-code-sonnet-5-no-skills/build-rls-003-org-roles-permissions.json" + }, + { + "experiment": "claude-code-sonnet-5-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "claude-code", + "modelProvider": "anthropic", + "modelId": "claude-sonnet-5", + "reasoningEffort": "high" + }, + "eval": "build-storage-001-private-bucket-access", + "stage": "build", + "product": [ + "storage", + "database" + ], + "topic": [ + "rls", + "sdk" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "bucket user-files exists", + "passed": true + }, + { + "name": "bucket user-files is private", + "passed": true + }, + { + "name": "RLS still enabled on storage.objects", + "passed": true + }, + { + "name": "user A lists only own files", + "passed": true, + "notes": "saw: 019fb0e7-0613-730a-ae13-0efd0c195137/receipt-alpha.pdf, 019fb0e7-0613-730a-ae13-0efd0c195137/receipt-beta.pdf" + }, + { + "name": "user B cannot read user A files", + "passed": true + }, + { + "name": "anon reads no files", + "passed": true + }, + { + "name": "user A can upload into own folder", + "passed": true + }, + { + "name": "user B cannot upload into user A folder", + "passed": true + }, + { + "name": "configured private per-user storage access", + "passed": true, + "judgeNotes": "Meets requirements: private user-files bucket, authenticated owner-scoped SELECT and INSERT policies with WITH CHECK on storage.objects, no RLS disabling or public access, and supabase-js createSignedUrl with expiry for sharing." + } + ], "skills": { "available": [], "loaded": [] @@ -7563,6 +7942,133 @@ "attempts": 2, "sourcePath": "codex-gpt-5.4-mini/build-functions-005-dual-auth-user-secret.json" }, + { + "experiment": "codex-gpt-5.4-mini", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase", + "supabase-postgres-best-practices" + ] + }, + "docs": { + "calls": [ + { + "source": "search_docs", + "query": "query { searchDocs(query: \"row level security organization admin editor viewer shared documents policies\", limit: 5) { nodes { title href content } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/local-development/testing/pgtap-extended", + "title": "Advanced pgTAP Testing" + }, + { + "url": "https://supabase.com/docs/guides/resources/glossary", + "title": "Glossary" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/features", + "title": "Features" + }, + { + "url": "https://supabase.com/docs/guides/storage/buckets/fundamentals", + "title": "Storage Buckets" + }, + { + "url": "https://supabase.com/docs/guides/self-hosting/self-hosted-s3", + "title": "Configure S3 Storage" + } + ], + "resultChars": 59205 + } + ] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.4-mini/build-rls-003-org-roles-permissions.json" + }, { "experiment": "codex-gpt-5.4-mini", "experimentSuite": "benchmark", @@ -9915,6 +10421,127 @@ "attempts": 2, "sourcePath": "codex-gpt-5.4-mini-no-skills/build-functions-005-dual-auth-user-secret.json" }, + { + "experiment": "codex-gpt-5.4-mini-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.4-mini", + "reasoningEffort": "medium" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [ + { + "source": "search_docs", + "query": "query {\n searchDocs(query: \"row level security policies memberships auth.uid organization role\", limit: 5) {\n nodes {\n ... on Guide {\n title\n href\n content\n }\n ... on TroubleshootingGuide {\n title\n href\n content\n }\n }\n }\n}", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", + "title": "Row Level Security" + }, + { + "url": "https://supabase.com/docs/guides/getting-started/features", + "title": "Features" + }, + { + "url": "https://supabase.com/docs/guides/database/postgres/column-level-security", + "title": "Column Level Security" + }, + { + "url": "https://supabase.com/docs/guides/realtime/getting_started", + "title": "Getting Started with Realtime" + }, + { + "url": "https://supabase.com/docs/guides/resources/glossary", + "title": "Glossary" + } + ], + "resultChars": 75044 + } + ] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.4-mini-no-skills/build-rls-003-org-roles-permissions.json" + }, { "experiment": "codex-gpt-5.4-mini-no-skills", "experimentSuite": "no-skills", @@ -12192,34 +12819,171 @@ "title": "Integrating With Supabase Auth" }, { - "url": "https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z", - "title": "Why is my service role key client getting RLS errors or not returning data?" + "url": "https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z", + "title": "Why is my service role key client getting RLS errors or not returning data?" + }, + { + "url": "https://supabase.com/docs/guides/auth/signing-keys", + "title": "JWT Signing Keys" + }, + { + "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", + "title": "Resumable WebSockets with Edge Functions" + }, + { + "url": "https://supabase.com/docs/guides/auth/jwt-fields", + "title": "JWT Claims Reference" + }, + { + "url": "https://supabase.com/docs/guides/functions/function-configuration", + "title": "Function Configuration" + } + ], + "resultChars": 114347 + } + ] + }, + "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", + "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", + "attempts": 2, + "sourcePath": "codex-gpt-5.6/build-functions-005-dual-auth-user-secret.json" + }, + { + "experiment": "codex-gpt-5.6", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.6-sol", + "reasoningEffort": "medium" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase", + "supabase-postgres-best-practices" + ] + }, + "docs": { + "calls": [ + { + "source": "shell_fetch", + "query": "/bin/bash -lc \"find .claude/skills/supabase-postgres-best-practices/references -maxdepth 1 -type f | sort | sed -n '/security/p' && curl -fsSL https://supabase.com/changelog.md | rg -n -i 'breaking-change|rls|row level|policy' | head -40\"", + "pages": [ + { + "url": "https://supabase.com/changelog.md" + } + ], + "resultChars": 8612 + }, + { + "source": "search_docs", + "query": "query { searchDocs(query: \"Row Level Security policies auth.uid organization membership SELECT UPDATE INSERT DELETE security definer\", limit: 5) { nodes { ... on Guide { title href content } ... on TroubleshootingGuide { title href content } } } }", + "hasContent": true, + "pages": [ + { + "url": "https://supabase.com/docs/guides/getting-started/features", + "title": "Features" }, { - "url": "https://supabase.com/docs/guides/auth/signing-keys", - "title": "JWT Signing Keys" + "url": "https://supabase.com/docs/guides/database/postgres/column-level-security", + "title": "Column Level Security" }, { - "url": "https://supabase.com/docs/guides/functions/examples/resumable-websockets", - "title": "Resumable WebSockets with Edge Functions" + "url": "https://supabase.com/docs/guides/database/postgres/row-level-security", + "title": "Row Level Security" }, { - "url": "https://supabase.com/docs/guides/auth/jwt-fields", - "title": "JWT Claims Reference" + "url": "https://supabase.com/docs/guides/resources/glossary", + "title": "Glossary" }, { - "url": "https://supabase.com/docs/guides/functions/function-configuration", - "title": "Function Configuration" + "url": "https://supabase.com/docs/guides/api/securing-your-api", + "title": "Securing your API" } ], - "resultChars": 114347 + "resultChars": 77235 } ] }, - "prompt": "Build and serve a Supabase Edge Function named `user-stats` for this project,\nreachable over HTTP at `/functions/v1/user-stats`.\n\nOur product stores per-user metrics in the existing `user_stats` table.\n\nTwo very different callers need to hit this one endpoint:\n\n1. **Our mobile app**, acting for a signed-in user. It sends that user's\n Supabase access token. The endpoint should return the user's own stats.\n\n2. **Our internal billing service**, a trusted backend with no signed-in user.\n It authenticates with the project's secret (service-role) key in the `apikey`\n header, and names the target user with a `user_id` in the JSON request body.\n It needs that user's stats.\n\nReturn the matching rows as JSON. The endpoint must be secure: only ever serve\nstats to a caller that is genuinely entitled to them, and turn away callers that\nare not.", - "promptSourcePath": "evals/build-functions-005-dual-auth-user-secret/PROMPT.md", - "attempts": 2, - "sourcePath": "codex-gpt-5.6/build-functions-005-dual-auth-user-secret.json" + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6/build-rls-003-org-roles-permissions.json" }, { "experiment": "codex-gpt-5.6", @@ -14730,6 +15494,98 @@ "attempts": 1, "sourcePath": "codex-gpt-5.6-no-skills/build-functions-005-dual-auth-user-secret.json" }, + { + "experiment": "codex-gpt-5.6-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "codex", + "modelProvider": "openai", + "modelId": "gpt-5.6-sol", + "reasoningEffort": "medium" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "codex-gpt-5.6-no-skills/build-rls-003-org-roles-permissions.json" + }, { "experiment": "codex-gpt-5.6-no-skills", "experimentSuite": "no-skills", @@ -16406,6 +17262,103 @@ "attempts": 1, "sourcePath": "opencode-kimi-k3/build-functions-005-dual-auth-user-secret.json" }, + { + "experiment": "opencode-kimi-k3", + "experimentSuite": "benchmark", + "experimentDisplay": { + "agent": "opencode", + "modelProvider": "moonshotai", + "modelId": "moonshotai/kimi-k3" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [ + "supabase", + "supabase-postgres-best-practices" + ], + "loaded": [ + "supabase", + "supabase-postgres-best-practices" + ] + }, + "docs": { + "calls": [] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "opencode-kimi-k3/build-rls-003-org-roles-permissions.json" + }, { "experiment": "opencode-kimi-k3", "experimentSuite": "benchmark", @@ -17974,6 +18927,97 @@ "attempts": 1, "sourcePath": "opencode-kimi-k3-no-skills/build-functions-005-dual-auth-user-secret.json" }, + { + "experiment": "opencode-kimi-k3-no-skills", + "experimentSuite": "no-skills", + "experimentDisplay": { + "agent": "opencode", + "modelProvider": "moonshotai", + "modelId": "moonshotai/kimi-k3" + }, + "eval": "build-rls-003-org-roles-permissions", + "stage": "build", + "product": [ + "database", + "auth" + ], + "topic": [ + "rls", + "security" + ], + "suite": "benchmark", + "interface": "mcp", + "passed": true, + "checks": [ + { + "name": "RLS enabled on documents", + "passed": true + }, + { + "name": "viewer sees only documents in their org", + "passed": true + }, + { + "name": "viewer cannot insert", + "passed": true + }, + { + "name": "editor can insert own org document", + "passed": true + }, + { + "name": "editor can update own document", + "passed": true + }, + { + "name": "editor cannot update another user's document", + "passed": true + }, + { + "name": "editor cannot delete another user's document", + "passed": true + }, + { + "name": "admin can update any document in their org", + "passed": true + }, + { + "name": "admin can delete any document in their org", + "passed": true + }, + { + "name": "admin cannot affect another org", + "passed": true + }, + { + "name": "WITH CHECK blocks editor from moving document to another org", + "passed": true + }, + { + "name": "cannot see another org's membership roster", + "passed": true + }, + { + "name": "a viewer role in one org doesn't grant admin power in another org", + "passed": true + }, + { + "name": "multi-org user can act as admin in the org where they hold that role", + "passed": true + } + ], + "skills": { + "available": [], + "loaded": [] + }, + "docs": { + "calls": [] + }, + "prompt": "Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org.", + "promptSourcePath": "evals/build-rls-003-org-roles-permissions/PROMPT.md", + "attempts": 1, + "sourcePath": "opencode-kimi-k3-no-skills/build-rls-003-org-roles-permissions.json" + }, { "experiment": "opencode-kimi-k3-no-skills", "experimentSuite": "no-skills", diff --git a/evals/build-rls-003-org-roles-permissions/EVAL.ts b/evals/build-rls-003-org-roles-permissions/EVAL.ts index ea7ee12b..dfc32011 100644 --- a/evals/build-rls-003-org-roles-permissions/EVAL.ts +++ b/evals/build-rls-003-org-roles-permissions/EVAL.ts @@ -1,227 +1,331 @@ -import type { CheckResult, ToolScorer } from '@supabase-evals/core'; +import type { + CheckResult, + ToolEvalContext, + ToolScorer, +} from '@supabase-evals/core'; +import { stripIndent } from 'common-tags'; const ORG_A = '11111111-1111-1111-1111-111111111111'; const ORG_B = '22222222-2222-2222-2222-222222222222'; const ADMIN_A = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'; const EDITOR_A = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb'; const VIEWER_A = 'cccccccc-cccc-cccc-cccc-cccccccccccc'; - -const asUser = ( - sub: string, - body: string, - finish: 'COMMIT' | 'ROLLBACK' = 'COMMIT' -) => ` -BEGIN; -SET LOCAL ROLE authenticated; -SET LOCAL request.jwt.claim.sub = '${sub}'; -SET LOCAL request.jwt.claim.role = 'authenticated'; -${body} -${finish}; -`; +const MULTI_ORG_USER = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'; const scorer: ToolScorer = async (ctx) => { - const q = (sql: string) => ctx.query(sql); - const checks: CheckResult[] = []; - - const resetTx = async () => { - try { - await q('ROLLBACK;'); - } catch { - // Clear aborted scorer transactions. - } - }; - try { - const { rows: rls } = await q( - `SELECT relname, relrowsecurity FROM pg_class WHERE relname IN ('documents', 'document_audit');` - ); - checks.push({ - name: 'RLS enabled on documents', - passed: rls.some( - (row) => row.relname === 'documents' && row.relrowsecurity === true - ), - }); - - const { rows: viewerReads } = await q( - asUser(VIEWER_A, `SELECT title FROM documents ORDER BY title;`) - ); - checks.push({ - name: 'viewer sees active org documents only', - passed: - viewerReads.length === 2 && - viewerReads.map((row) => row.title).join(',') === - 'Admin plan,Editor draft', - }); - - let viewerInsertBlocked = false; - try { - await q( - asUser( - VIEWER_A, - ` -INSERT INTO documents (org_id, owner_id, title, body) -VALUES ('${ORG_A}', '${VIEWER_A}', 'viewer insert', 'should fail'); - ` - ) - ); - } catch { - viewerInsertBlocked = true; - await resetTx(); - } - checks.push({ name: 'viewer cannot insert', passed: viewerInsertBlocked }); - - const { rows: editorInsert } = await q( - asUser( - EDITOR_A, - ` -INSERT INTO documents (org_id, owner_id, title, body) -VALUES ('${ORG_A}', '${EDITOR_A}', 'editor insert', 'allowed') -RETURNING id; - `, - 'ROLLBACK' - ) - ); - checks.push({ - name: 'editor can insert own org document', - passed: editorInsert.length === 1, - }); - - const { rows: editorOwnUpdate } = await q( - asUser( - EDITOR_A, - ` -UPDATE documents -SET body = 'editor changed own document' -WHERE id = '10000000-0000-0000-0000-000000000002' -RETURNING id; - `, - 'ROLLBACK' - ) - ); - checks.push({ - name: 'editor can update own document', - passed: editorOwnUpdate.length === 1, - }); - - const { rows: editorUpdatesAdmin } = await q( - asUser( - EDITOR_A, - ` -UPDATE documents -SET body = 'editor changed admin document' -WHERE id = '10000000-0000-0000-0000-000000000001' -RETURNING id; - `, - 'ROLLBACK' - ) - ); - checks.push({ - name: "editor cannot update another user's document", - passed: editorUpdatesAdmin.length === 0, - }); - - await q( - asUser( - ADMIN_A, - ` -DELETE FROM documents -WHERE id = '10000000-0000-0000-0000-000000000001'; - ` - ) - ); - const { rows: adminSoftDelete } = await q( - `SELECT id, deleted_at FROM documents WHERE id = '10000000-0000-0000-0000-000000000001';` - ); - checks.push({ - name: 'admin delete soft-deletes document in org', - passed: - adminSoftDelete.length === 1 && - adminSoftDelete[0]?.id === '10000000-0000-0000-0000-000000000001' && - Boolean(adminSoftDelete[0]?.deleted_at), - }); - - const { rows: adminCrossOrg } = await q( - asUser( - ADMIN_A, - ` -UPDATE documents -SET deleted_at = now() -WHERE id = '20000000-0000-0000-0000-000000000001' -RETURNING id; - `, - 'ROLLBACK' - ) - ); - checks.push({ - name: 'admin cannot affect another org', - passed: adminCrossOrg.length === 0, - }); - - let orgReassignmentBlocked = false; - try { - const { rows } = await q( - asUser( - EDITOR_A, - ` -UPDATE documents -SET org_id = '${ORG_B}' -WHERE id = '10000000-0000-0000-0000-000000000002' -RETURNING id; - `, - 'ROLLBACK' - ) - ); - orgReassignmentBlocked = rows.length === 0; - } catch { - orgReassignmentBlocked = true; - await resetTx(); - } - checks.push({ - name: 'WITH CHECK blocks editor from moving document to another org', - passed: orgReassignmentBlocked, - }); - - const { rows: auditedUpdate } = await q( - asUser( - EDITOR_A, - ` -UPDATE documents -SET body = 'audit verifier' -WHERE id = '10000000-0000-0000-0000-000000000002' -RETURNING id; - ` - ) - ); - const { rows: auditRows } = await q( - `SELECT actor_id, document_id FROM document_audit WHERE document_id = '10000000-0000-0000-0000-000000000002';` - ); - checks.push({ - name: 'write creates audit row with acting user', - passed: - auditedUpdate.length === 1 && - auditRows.some( - (row) => - row.actor_id === EDITOR_A && - row.document_id === '10000000-0000-0000-0000-000000000002' - ), - }); + const checks: CheckResult[] = [ + await checkRlsEnabled(ctx), + await checkViewerSeesOnlyOwnOrgDocuments(ctx), + await checkViewerCannotInsert(ctx), + await checkEditorCanInsertOwnOrgDocument(ctx), + await checkEditorCanUpdateOwnDocument(ctx), + await checkEditorCannotUpdateAnotherUsersDocument(ctx), + await checkEditorCannotDeleteAnotherUsersDocument(ctx), + await checkAdminCanUpdateAnyDocumentInOrg(ctx), + await checkAdminCanDeleteAnyDocumentInOrg(ctx), + await checkAdminCannotAffectAnotherOrg(ctx), + await checkWithCheckBlocksOrgReassignment(ctx), + await checkCannotSeeAnotherOrgsMembershipRoster(ctx), + await checkOtherOrgRoleDoesNotLeakIn(ctx), + await checkOwnOrgAdminRoleStillWorks(ctx), + ]; + return { passed: checks.every((c) => c.passed), checks }; } catch (error) { const msg = error instanceof Error ? error.message : String(error); - checks.push({ - name: 'scorer evaluated org role RLS', - passed: false, - notes: msg, - }); return { passed: false, - checks, + checks: [ + { name: 'scorer evaluated org role RLS', passed: false, notes: msg }, + ], }; } +}; +export default scorer; + +type UserQueryResult = { rows: Record[]; error: Error | null }; + +/** + * Runs a query as a given user, via the request.jwt.claim.sub/role + * technique shown in RLS testing guide: + * (https://supabase.com/docs/guides/local-development/testing/overview). + * + * Returns errors (incl. RLS-blocked writes) as values instead of throwing. + */ +async function runAsUser( + ctx: ToolEvalContext, + sub: string, + body: string, + finish: 'COMMIT' | 'ROLLBACK' = 'COMMIT' +): Promise { + try { + const { rows } = await ctx.query(stripIndent` + BEGIN; + SET LOCAL ROLE authenticated; + SET LOCAL request.jwt.claim.sub = '${sub}'; + SET LOCAL request.jwt.claim.role = 'authenticated'; + ${body} + ${finish}; + `); + return { rows, error: null }; + } catch (error) { + await ctx.query('ROLLBACK;').catch(() => {}); + return { + rows: [], + error: error instanceof Error ? error : new Error(String(error)), + }; + } +} + +async function checkRlsEnabled(ctx: ToolEvalContext): Promise { + const { rows } = await ctx.query( + `SELECT relrowsecurity FROM pg_class WHERE relname = 'documents';` + ); return { - passed: checks.every((check) => check.passed), - checks, + name: 'RLS enabled on documents', + passed: rows[0]?.relrowsecurity === true, }; -}; +} -export default scorer; +async function checkViewerSeesOnlyOwnOrgDocuments( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + VIEWER_A, + `SELECT title FROM documents ORDER BY title;` + ); + return { + name: 'viewer sees only documents in their org', + passed: + !result.error && + result.rows.length === 2 && + result.rows.map((row) => row.title).join(',') === + 'Admin plan,Editor draft', + }; +} + +async function checkViewerCannotInsert( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + VIEWER_A, + stripIndent` + INSERT INTO documents (org_id, owner_id, title, body) + VALUES ('${ORG_A}', '${VIEWER_A}', 'viewer insert', 'should fail'); + ` + ); + return { name: 'viewer cannot insert', passed: Boolean(result.error) }; +} + +async function checkEditorCanInsertOwnOrgDocument( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + EDITOR_A, + stripIndent` + INSERT INTO documents (org_id, owner_id, title, body) + VALUES ('${ORG_A}', '${EDITOR_A}', 'editor insert', 'allowed') + RETURNING id; + `, + 'ROLLBACK' + ); + return { + name: 'editor can insert own org document', + passed: !result.error && result.rows.length === 1, + }; +} + +async function checkEditorCanUpdateOwnDocument( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + EDITOR_A, + stripIndent` + UPDATE documents + SET body = 'editor changed own document' + WHERE id = '10000000-0000-0000-0000-000000000002' + RETURNING id; + `, + 'ROLLBACK' + ); + return { + name: 'editor can update own document', + passed: !result.error && result.rows.length === 1, + }; +} + +async function checkEditorCannotUpdateAnotherUsersDocument( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + EDITOR_A, + stripIndent` + UPDATE documents + SET body = 'editor changed admin document' + WHERE id = '10000000-0000-0000-0000-000000000001' + RETURNING id; + `, + 'ROLLBACK' + ); + return { + name: "editor cannot update another user's document", + passed: Boolean(result.error) || result.rows.length === 0, + }; +} + +async function checkEditorCannotDeleteAnotherUsersDocument( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + EDITOR_A, + `DELETE FROM documents WHERE id = '10000000-0000-0000-0000-000000000001' RETURNING id;`, + 'ROLLBACK' + ); + return { + name: "editor cannot delete another user's document", + passed: Boolean(result.error) || result.rows.length === 0, + }; +} + +async function checkAdminCanUpdateAnyDocumentInOrg( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + ADMIN_A, + stripIndent` + UPDATE documents + SET body = 'admin changed editor document' + WHERE id = '10000000-0000-0000-0000-000000000002' + RETURNING id; + `, + 'ROLLBACK' + ); + return { + name: 'admin can update any document in their org', + passed: !result.error && result.rows.length === 1, + }; +} + +async function checkAdminCanDeleteAnyDocumentInOrg( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + ADMIN_A, + `DELETE FROM documents WHERE id = '10000000-0000-0000-0000-000000000002' RETURNING id;`, + 'ROLLBACK' + ); + return { + name: 'admin can delete any document in their org', + passed: !result.error && result.rows.length === 1, + }; +} + +async function checkAdminCannotAffectAnotherOrg( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + ADMIN_A, + stripIndent` + UPDATE documents + SET body = 'admin touched another org' + WHERE id = '20000000-0000-0000-0000-000000000001' + RETURNING id; + `, + 'ROLLBACK' + ); + return { + name: 'admin cannot affect another org', + passed: Boolean(result.error) || result.rows.length === 0, + }; +} + +async function checkWithCheckBlocksOrgReassignment( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + EDITOR_A, + stripIndent` + UPDATE documents + SET org_id = '${ORG_B}' + WHERE id = '10000000-0000-0000-0000-000000000002' + RETURNING id; + `, + 'ROLLBACK' + ); + return { + name: 'WITH CHECK blocks editor from moving document to another org', + passed: Boolean(result.error) || result.rows.length === 0, + }; +} + +async function checkCannotSeeAnotherOrgsMembershipRoster( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + EDITOR_A, + `SELECT user_id FROM memberships WHERE org_id = '${ORG_B}';` + ); + return { + name: "cannot see another org's membership roster", + passed: Boolean(result.error) || result.rows.length === 0, + }; +} + +/** + * Checks that a user's admin role in org B doesn't leak into org A, where + * they're only a viewer. In the memberships table, role is a column on the + * (user_id, org_id) row, not the user, so the same user can hold a + * different role per org. This checks if policies actually join on + * org_id instead of just checking the user's role anywhere. + */ +async function checkOtherOrgRoleDoesNotLeakIn( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + MULTI_ORG_USER, + stripIndent` + UPDATE documents + SET body = 'multi-org user tried to act as admin here' + WHERE id = '10000000-0000-0000-0000-000000000001' + RETURNING id; + `, + 'ROLLBACK' + ); + return { + name: "a viewer role in one org doesn't grant admin power in another org", + passed: Boolean(result.error) || result.rows.length === 0, + }; +} + +async function checkOwnOrgAdminRoleStillWorks( + ctx: ToolEvalContext +): Promise { + const result = await runAsUser( + ctx, + MULTI_ORG_USER, + stripIndent` + UPDATE documents + SET body = 'multi-org user acting as admin in their own org' + WHERE id = '20000000-0000-0000-0000-000000000001' + RETURNING id; + `, + 'ROLLBACK' + ); + return { + name: 'multi-org user can act as admin in the org where they hold that role', + passed: !result.error && result.rows.length === 1, + }; +} diff --git a/evals/build-rls-003-org-roles-permissions/PROMPT.md b/evals/build-rls-003-org-roles-permissions/PROMPT.md index 75256878..e80cc53c 100644 --- a/evals/build-rls-003-org-roles-permissions/PROMPT.md +++ b/evals/build-rls-003-org-roles-permissions/PROMPT.md @@ -1,6 +1,6 @@ --- stage: build -suite: other +suite: benchmark interface: mcp product: - database @@ -8,31 +8,7 @@ product: topic: - rls - security +motivation: AI-1006, FDBKIN-19292 --- -You are working on a Supabase project for a multi-tenant document app. - -The schema has three tables already created and seeded: - -```sql --- memberships -user_id uuid, org_id uuid, role text - --- documents -id uuid, org_id uuid, owner_id uuid, title text, body text, deleted_at timestamptz - --- document_audit -id uuid, document_id uuid, actor_id uuid, action text, ts timestamptz -``` - -Add the RLS policies and database logic needed for authenticated users: - -1. Viewers can read active documents in orgs where they are members. -2. Editors can read active documents in their orgs, insert documents they own in their orgs, and update or delete only documents they own. -3. Admins can read, update, and delete any active document in orgs where they are admins. -4. Soft-deleted documents (`deleted_at IS NOT NULL`) should not be visible through normal reads. -5. Deletes should be soft deletes by setting `deleted_at`; do not hard-delete rows. -6. Every insert, update, and soft-delete should write a row to `document_audit` with the acting user. - -Apply the required database changes. End your turn when you believe the -policies and audit behavior are in place. +Access control on our shared docs feature needs work, people can see and edit documents they shouldn't. Viewers should just be able to read, editors should only manage their own docs, and admins should be able to manage anything in their org. diff --git a/evals/build-rls-003-org-roles-permissions/remote/project.sql b/evals/build-rls-003-org-roles-permissions/remote/project.sql index 3a3c9625..79ed8bed 100644 --- a/evals/build-rls-003-org-roles-permissions/remote/project.sql +++ b/evals/build-rls-003-org-roles-permissions/remote/project.sql @@ -10,30 +10,27 @@ CREATE TABLE documents ( org_id uuid NOT NULL, owner_id uuid NOT NULL, title text NOT NULL, - body text NOT NULL, - deleted_at timestamptz -); - -CREATE TABLE document_audit ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - document_id uuid NOT NULL, - actor_id uuid NOT NULL, - action text NOT NULL, - ts timestamptz NOT NULL DEFAULT now() + body text NOT NULL ); GRANT SELECT ON memberships TO authenticated; GRANT SELECT, INSERT, UPDATE, DELETE ON documents TO authenticated; -GRANT SELECT, INSERT ON document_audit TO authenticated; + +-- Mimics someone accepting Studio's default read-access policy template +-- without customizing it. Permissive policies OR together in Postgres RLS, +-- so this stays a live hole unless the agent notices and drops it. +ALTER TABLE documents ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Enable read access for all users" ON documents FOR SELECT USING (true); INSERT INTO memberships (user_id, org_id, role) VALUES ('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '11111111-1111-1111-1111-111111111111', 'admin'), ('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '11111111-1111-1111-1111-111111111111', 'editor'), ('cccccccc-cccc-cccc-cccc-cccccccccccc', '11111111-1111-1111-1111-111111111111', 'viewer'), - ('dddddddd-dddd-dddd-dddd-dddddddddddd', '22222222-2222-2222-2222-222222222222', 'editor'); + ('dddddddd-dddd-dddd-dddd-dddddddddddd', '22222222-2222-2222-2222-222222222222', 'editor'), + ('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', '11111111-1111-1111-1111-111111111111', 'viewer'), + ('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', '22222222-2222-2222-2222-222222222222', 'admin'); -INSERT INTO documents (id, org_id, owner_id, title, body, deleted_at) VALUES - ('10000000-0000-0000-0000-000000000001', '11111111-1111-1111-1111-111111111111', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Admin plan', 'org A admin document', NULL), - ('10000000-0000-0000-0000-000000000002', '11111111-1111-1111-1111-111111111111', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Editor draft', 'org A editor document', NULL), - ('10000000-0000-0000-0000-000000000003', '11111111-1111-1111-1111-111111111111', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Deleted draft', 'org A deleted document', now()), - ('20000000-0000-0000-0000-000000000001', '22222222-2222-2222-2222-222222222222', 'dddddddd-dddd-dddd-dddd-dddddddddddd', 'Org B draft', 'org B editor document', NULL); +INSERT INTO documents (id, org_id, owner_id, title, body) VALUES + ('10000000-0000-0000-0000-000000000001', '11111111-1111-1111-1111-111111111111', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 'Admin plan', 'org A admin document'), + ('10000000-0000-0000-0000-000000000002', '11111111-1111-1111-1111-111111111111', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'Editor draft', 'org A editor document'), + ('20000000-0000-0000-0000-000000000001', '22222222-2222-2222-2222-222222222222', 'dddddddd-dddd-dddd-dddd-dddddddddddd', 'Org B draft', 'org B editor document');