permdock 0.6.0 → 0.7.0
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.
- package/README.md +1 -0
- package/dist/{2020-C9VN-x8c.js → 2020-CJbO8Bvz.js} +1 -3
- package/dist/a2a/index.d.ts +5 -5
- package/dist/a2a/index.js +4 -4
- package/dist/{adoption-scan-DCYe60Pi.js → adoption-scan-D-feE7BE.js} +1 -1
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +1 -1
- package/dist/ai-sdk/index.d.ts +21 -6
- package/dist/ai-sdk/index.js +19 -9
- package/dist/{approval-prompt-DvSSPQJX.d.ts → approval-prompt-K0faOLFp.d.ts} +2 -2
- package/dist/approvals/index.d.ts +4 -4
- package/dist/approvals/index.js +4 -4
- package/dist/{approvals-p1PGoVjb.js → approvals-CMRfsbBh.js} +74 -48
- package/dist/{arazzo-41WuzHou.js → arazzo-ClrF3PVb.js} +2 -2
- package/dist/{arazzo-CaUVBn7D.js → arazzo-rdeJ8n1J.js} +4 -4
- package/dist/{args-IIolxxKA.js → args-CC3OwyHW.js} +112 -2
- package/dist/astro/index.d.ts +6 -6
- package/dist/astro/index.js +2 -2
- package/dist/{audience-DabClFLQ.js → audience-BrMs8c4o.js} +1 -1
- package/dist/{auth-DRLEizbL.js → auth-CscUX8Sy.js} +3 -4
- package/dist/authzen/index.d.ts +162 -22
- package/dist/authzen/index.js +315 -146
- package/dist/{automation-BNM3TtDo.d.ts → automation-DOtAFWdy.d.ts} +3 -3
- package/dist/{automation-DVjOckjV.js → automation-ySsxp_Yp.js} +1 -1
- package/dist/base-url-BQbddZO5.js +9 -0
- package/dist/better-auth/index.d.ts +2 -2
- package/dist/better-auth/index.js +2 -2
- package/dist/better-supabase/index.d.ts +50 -8
- package/dist/better-supabase/index.js +94 -32
- package/dist/{bind-CAvgUuKs.js → bind-D1nO6fiC.js} +5 -5
- package/dist/{bind-DOyrv6zi.d.ts → bind-JDuvxjuW.d.ts} +7 -7
- package/dist/{build-instance-Bu_eDqYw.js → build-instance-DxE7czo9.js} +1 -1
- package/dist/{canonical-json-BC8_nMTw.js → canonical-json-DwilZ1iB.js} +6 -3
- package/dist/{capability-Drg-1YhL.d.ts → capability-D4UC9TTW.d.ts} +2 -2
- package/dist/{capability-N1K9IpqS.js → capability-L7NnfhiE.js} +1 -1
- package/dist/{casbin-Cr7RLtA3.js → casbin-BHijLuHY.js} +2 -2
- package/dist/{casbin-Dyu2gnUD.js → casbin-BW9pS47J.js} +1 -1
- package/dist/{casl-64gpT-8x.js → casl-CmJlgziS.js} +2 -2
- package/dist/{casl-DjEDhTCP.js → casl-UbI8v-Wq.js} +1 -1
- package/dist/catalog/index.d.ts +70 -10
- package/dist/catalog/index.js +112 -6
- package/dist/catalog-BCCjSAdH.js +1 -0
- package/dist/{catalog-DZVbrF-z.js → catalog-BaMQfQ6Y.js} +3 -3
- package/dist/{catalog-HD2gf5mY.js → catalog-D69WhFii.js} +43 -13
- package/dist/{catalog-doc-Cdn7-0pQ.js → catalog-doc-CV9s6B1S.js} +28 -5
- package/dist/{catalog-fingerprint-CCP0-yQS.js → catalog-fingerprint-C144oW4M.js} +1 -1
- package/dist/{create-CQWIpUG-.js → challenge-nGWma08S.js} +122 -25
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/index.js +2 -2
- package/dist/chat-sdk/index.d.ts +3 -3
- package/dist/checks-BC_0mPIq.js +37 -0
- package/dist/claude-agent/index.d.ts +3 -3
- package/dist/claude-agent/index.js +2 -2
- package/dist/clerk/index.d.ts +3 -3
- package/dist/clerk/index.js +1 -1
- package/dist/cli/bin.js +2 -2
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +3 -3
- package/dist/cloud/index.d.ts +127 -11
- package/dist/cloud/index.js +251 -3
- package/dist/{cloud-ytQ3uviA.js → cloud-CO8pYtwr.js} +5 -5
- package/dist/{codemod-ua2hHy7w.js → codemod-BP6n8N9L.js} +3 -3
- package/dist/{codemod-5mpSpnUY.js → codemod-i4N2BhDc.js} +3 -3
- package/dist/{collect-mLTY4nwb.js → collect-BaWeMjwV.js} +17 -8
- package/dist/{collect-Ci6akVwH.js → collect-DNm3oKOk.js} +6 -5
- package/dist/collect-k_Dulxr5.js +1 -0
- package/dist/compile/index.d.ts +1 -1
- package/dist/compile/index.js +1 -1
- package/dist/{compile-CMqMx_IN.js → compile-BAEO5bOk.js} +2 -2
- package/dist/{compile-1bsAezL0.d.ts → compile-COM2VKuN.d.ts} +3 -3
- package/dist/{completion-BTFrHZhX.js → completion-BNdVf7X2.js} +3 -3
- package/dist/{config-bs_kZeYq.js → config-CoQHDrDi.js} +7 -3
- package/dist/{config-Bj2Ixq3d.js → config-DGZzMV1B.js} +2 -2
- package/dist/{config-report-DSpsLpCE.js → config-report-Cm7UITNA.js} +3 -3
- package/dist/connector-schema-_VCSsdtD.js +433 -0
- package/dist/{context-B_u3_bDJ.js → context--uM9V3Ui.js} +2 -2
- package/dist/convex/index.d.ts +5 -5
- package/dist/convex/index.js +2 -2
- package/dist/core-B5-w6Kw9.js +335 -0
- package/dist/core-CQkE8Esr.d.ts +179 -0
- package/dist/{create-B30OMUOJ.d.ts → create-B2-CQqCI.d.ts} +5 -5
- package/dist/{create-CGm28gyI.js → create-Cb86Z8Im.js} +9 -8
- package/dist/{create-DCF2SvoT.js → create-DJrlhEp9.js} +5 -5
- package/dist/{create-D_NmPOdH.d.ts → create-QHuuPw3q.d.ts} +7 -7
- package/dist/{create-CBMmFie-.d.ts → create-UTnUVETj.d.ts} +30 -10
- package/dist/{credential-BcoiejpY.js → credential-CIM7wHwA.js} +4 -4
- package/dist/{credentials-DPyHmYz5.js → credentials-BpuTwd7a.js} +1 -1
- package/dist/{deciding-columns-Sq12mEA6.js → deciding-columns-DRo2QM6E.js} +13 -2
- package/dist/{delegation-j6-rc37o.js → delegation-C5rOdTp5.js} +5 -5
- package/dist/delivery/index.d.ts +5 -5
- package/dist/delivery/index.js +4 -3
- package/dist/{delivery-DCAgNyCn.js → delivery-rwOKLWRm.js} +51 -2
- package/dist/{denied-text-CiFQNuMV.d.ts → denied-text-D4qkEtFz.d.ts} +2 -2
- package/dist/{describe-BybMRY7A.js → describe-DZlnEtLy.js} +1 -0
- package/dist/{describe-B5qgDvEW.d.ts → describe-O0SwYIK8.d.ts} +2 -2
- package/dist/{diff-CmuSD3vc.js → diff-Btr1c1Ow.js} +2 -2
- package/dist/{diff-3jbx6Rm4.js → diff-DzT-xD13.js} +10 -10
- package/dist/{doctor-BdZw53-k.js → doctor-BU1szn63.js} +241 -49
- package/dist/{doctor-3ZqNHX_G.js → doctor-BoK_y0KO.js} +4 -4
- package/dist/drizzle/index.d.ts +3 -3
- package/dist/drizzle/index.js +1 -1
- package/dist/elysia/index.d.ts +5 -5
- package/dist/elysia/index.js +3 -3
- package/dist/{errors-DH5QEGXh.d.ts → errors-B_Qg9D3e.d.ts} +2 -2
- package/dist/{errors-CWTY6uih.js → errors-DpP_U6Bf.js} +1 -0
- package/dist/{evaluate-CNClBvcj.js → evaluate-B_oqFbWC.js} +55 -12
- package/dist/{evaluations-C_1vdSZc.d.ts → evaluations-BCddEOfO.d.ts} +4 -4
- package/dist/{evaluations-beAooV0n.js → evaluations-DReoiVBw.js} +4 -4
- package/dist/eve/index.d.ts +5 -5
- package/dist/eve/index.js +3 -3
- package/dist/express/index.d.ts +5 -5
- package/dist/express/index.js +3 -3
- package/dist/fastify/index.d.ts +5 -5
- package/dist/fastify/index.js +3 -3
- package/dist/{fetch-adapter-Dw0MTS3D.d.ts → fetch-adapter-CuhyYwC1.d.ts} +4 -4
- package/dist/{fetch-adapter-DD_gfmrC.js → fetch-adapter-De-u6vhJ.js} +3 -3
- package/dist/{fields-OYG_JR78.js → fields-ClcQLrVc.js} +48 -13
- package/dist/{files-C5ZI27eV.js → files-acUbNwWt.js} +15 -2
- package/dist/{fixtures-B7XrLeY7.js → fixtures-BTMW6zgU.js} +2 -2
- package/dist/{flags-C3DktBDY.js → flags-aaeRAdiD.js} +2 -2
- package/dist/{from-snapshot-CTCMsFZk.js → from-snapshot-Bz469dIh.js} +17 -10
- package/dist/generate-3FNO5P5Q.js +159 -0
- package/dist/{grantee-hxrUsbpM.js → grantee-BrRANrpT.js} +1 -1
- package/dist/{grantee-conditions-CcXf8hMM.js → grantee-conditions-C6P1Tvol.js} +1 -1
- package/dist/{grantee-match-DbENBHLB.js → grantee-match-0RtHWQDx.js} +3 -3
- package/dist/h3/index.d.ts +4 -4
- package/dist/h3/index.js +3 -3
- package/dist/{helpers-CBYVMjwh.js → helpers-DY3vXuGs.js} +3 -3
- package/dist/hono/index.d.ts +5 -5
- package/dist/hono/index.js +3 -3
- package/dist/{hosted-BPk51-kX.d.ts → hosted-BmjzxQhp.d.ts} +14 -2
- package/dist/{hosted-B46-5LYE.js → hosted-XBgU6RGT.js} +34 -12
- package/dist/{http-DXJ37RDA.js → http-Ca3Sr5x1.js} +1 -1
- package/dist/{import-wyqXUyjm.js → import-Bz0GefWg.js} +5 -5
- package/dist/{import-CNV7I2Dw.js → import-DB2EG_iy.js} +3 -3
- package/dist/{index-C-6Letxg.d.ts → index-BE8y3moU.d.ts} +50 -8
- package/dist/{index-B8YjkEZ4.d.ts → index-BSfRl0hZ.d.ts} +6 -6
- package/dist/{index-DuWxc5dt.d.ts → index-BTkwrK7v.d.ts} +143 -14
- package/dist/index-CMiPH2Tv.d.ts +206 -0
- package/dist/{index-Ch8LpNDp.d.ts → index-DH9DUDEM.d.ts} +18 -3
- package/dist/{index-CueFnxQc.d.ts → index-DLwcuCN_.d.ts} +86 -7
- package/dist/index.d.ts +18 -17
- package/dist/index.js +27 -25
- package/dist/{init-GG6Jh8HD.js → init-5TXNji88.js} +5 -5
- package/dist/{init-DeMYsnYJ.js → init-B-4t1NzC.js} +4 -4
- package/dist/{instance-options-BsVpyGIk.d.ts → instance-options-CU0YQCLH.d.ts} +1 -1
- package/dist/{instance-snapshot-BOEhcqIF.js → instance-snapshot-CjcwnEiV.js} +16 -11
- package/dist/jwt/index.d.ts +83 -5
- package/dist/jwt/index.js +143 -5
- package/dist/{kernel-D0NVb1cK.js → kernel-DVExUwkp.js} +138 -12
- package/dist/kysely/index.d.ts +3 -3
- package/dist/kysely/index.js +1 -1
- package/dist/load-DrMHXzgN.js +1 -0
- package/dist/{load-DFS_ry0W.js → load-DxroEzH1.js} +2 -2
- package/dist/{login-Dfe5RlYk.js → login-ClSJh5t5.js} +4 -4
- package/dist/{login-imhuwvn3.js → login-WC9HPJpU.js} +6 -10
- package/dist/{manifest-C8IzW3Do.js → manifest-B79YuF2T.js} +6 -2
- package/dist/{manifest-mx5ghSFM.d.ts → manifest-BVhExYhw.d.ts} +23 -2
- package/dist/{manifest-dvL2XEe1.js → manifest-Dv6q4i5s.js} +54 -35
- package/dist/{manifest-schema-C0kK_8V0.js → manifest-schema-Cv492Qev.js} +37 -7
- package/dist/{map-claims-D3oRmTk0.js → map-claims-etGIS_Dv.js} +14 -11
- package/dist/{map-error-Dr70oA_3.js → map-error-BQGyDm_M.js} +1 -1
- package/dist/mcp/index.d.ts +64 -10
- package/dist/mcp/index.js +151 -22
- package/dist/{migrate-CIq4-E9F.js → migrate-Dodel0IJ.js} +77 -24
- package/dist/nest/index.d.ts +5 -5
- package/dist/nest/index.js +3 -3
- package/dist/next/index.d.ts +9 -9
- package/dist/next/index.js +5 -5
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +2 -2
- package/dist/node/index.d.ts +5 -5
- package/dist/node/index.js +2 -2
- package/dist/openai/index.d.ts +5 -5
- package/dist/openai/index.js +3 -3
- package/dist/openapi/index.d.ts +14 -4
- package/dist/openapi/index.js +1 -1
- package/dist/{openapi-C4dDTVEd.js → openapi-DMocl1bh.js} +4 -4
- package/dist/{openapi-B7HZ0f-r.js → openapi-Ddb4628T.js} +2 -7
- package/dist/{openapi-DD1SV-5-.js → openapi-ztVaux6j.js} +8 -8
- package/dist/{operation-findings-DgATDpvh.js → operation-findings-kRcEp8lE.js} +1 -1
- package/dist/{operations-X0VM7F3m.d.ts → operations-BNLes61x.d.ts} +1 -1
- package/dist/orpc/index.d.ts +6 -6
- package/dist/orpc/index.js +4 -4
- package/dist/otel/index.d.ts +3 -3
- package/dist/otel/index.js +1 -1
- package/dist/{output-1im__-08.js → output-DgSDLrta.js} +7 -3
- package/dist/{output-files-DhRBBE4n.js → output-files-CCMdcFWb.js} +2 -2
- package/dist/output-files-DPAY7-GX.js +1 -0
- package/dist/{ownership-DqaJNJPn.js → ownership-Z2LpxsGa.js} +443 -40
- package/dist/parse-Ddhz2xf4.js +148 -0
- package/dist/{parse-local-manifest-CVSs6J9I.js → parse-local-manifest-T1Q8X3A2.js} +24 -1
- package/dist/{parse-local-manifest-5ODwqu8b.d.ts → parse-local-manifest-e4mdwAKd.d.ts} +1 -1
- package/dist/pdp/index.d.ts +3 -3
- package/dist/pdp/index.js +7 -3
- package/dist/peer-12Q5I_RH.js +976 -0
- package/dist/{peer-CRvC7Hg1.js → peer-DOzzMfZL.js} +1 -1
- package/dist/{permdock-BrAscV28.js → permdock-BfWEeMqk.js} +177 -49
- package/dist/{permdock-SV8ZINYT.d.ts → permdock-Dco6dyCK.d.ts} +34 -5
- package/dist/{permission-registry-Br4jSNVi.js → permission-registry-DpPtSKNZ.js} +1 -1
- package/dist/{vocabulary-CN8H_9UF.d.ts → permission-types-DhNEmoRG.d.ts} +4 -64
- package/dist/{permissions-u_KHg-sN.d.ts → permissions-CoqCOfJ3.d.ts} +2 -1
- package/dist/{permissions-B-wCP5ZC.js → permissions-CyH2MV_B.js} +1 -1
- package/dist/{permissions-BPxlwVhN.js → permissions-DArULThq.js} +4 -4
- package/dist/pkce-5h8NgMQJ.js +21 -0
- package/dist/{policies-DP2nqRoF.js → policies-B2lZIurj.js} +3 -3
- package/dist/{policy-77FSAq_V.d.ts → policy-B7dg_yzu.d.ts} +2 -2
- package/dist/{policy-DbM5YYtV.js → policy-DaJy4nK4.js} +17 -8
- package/dist/{policy-from-catalog-C2gMa2Dj.js → policy-from-catalog-DFWYkNdu.js} +9 -9
- package/dist/{policy-types-n0uRTsW8.d.ts → policy-types-B-F-qAZk.d.ts} +83 -4
- package/dist/postgres-store-BL9M_CA1.js +1001 -0
- package/dist/{postgrest-CEgRbefE.d.ts → postgrest-CYacM_u7.d.ts} +12 -1
- package/dist/{postgrest-DAjUkF9w.js → postgrest-r5faDIzD.js} +37 -4
- package/dist/{powersync-DISt6SUe.js → powersync-13odBwiM.js} +3 -3
- package/dist/powersync-53ryOZip.js +1059 -0
- package/dist/powersync-CExtTLyA.js +1 -0
- package/dist/preset/index.js +1 -1
- package/dist/{preset-DZdU0tne.js → preset-CHTbd-DQ.js} +2 -2
- package/dist/prisma/index.d.ts +2 -2
- package/dist/prisma/index.js +1 -1
- package/dist/{problem-details-B45V5d2b.d.ts → problem-details-Tcfmj3bV.d.ts} +1 -1
- package/dist/prompt-DwtVemER.js +935 -0
- package/dist/{protected-RFGpakN9.d.ts → protected-BwZYL1X_.d.ts} +6 -5
- package/dist/{protected-BlF0F6wL.js → protected-DXE3cQYg.js} +2 -2
- package/dist/{provider-B1al-AZz.js → provider-BQsTYYy0.js} +2 -2
- package/dist/{provider-DILL9oPK.d.ts → provider-Csk-THAn.d.ts} +1 -1
- package/dist/{provider-store-DM6DNcta.js → provider-store-IgqQipCO.js} +2 -2
- package/dist/{pull-jEwbf80J.js → pull-CN5OLxrF.js} +7 -7
- package/dist/pull-Cty6Ovyu.js +1 -0
- package/dist/{push-BD4f15co.js → push-tVdG8dUL.js} +54 -14
- package/dist/{pushed-policy-DCW91KQU.d.ts → pushed-policy-BbcY9r_4.d.ts} +1 -1
- package/dist/{pushed-policy-CqtxkV4G.js → pushed-policy-CmYeSQqq.js} +1 -1
- package/dist/react/index.d.ts +6 -6
- package/dist/react/index.js +3 -3
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +25 -11
- package/dist/react-native/index.js +209 -21
- package/dist/{refs-K6eWUJ0H.js → refs-CNJBiTFo.js} +15 -1
- package/dist/{replay-Bkm-Ne4l.d.ts → replay-DCDD_ZfL.d.ts} +1 -1
- package/dist/{request-context-C81Pip9n.js → request-context-BykKLmyE.js} +2 -2
- package/dist/resend/index.d.ts +4 -4
- package/dist/{retire-CALjIQ7e.js → retire-Wj6dkyDE.js} +2 -2
- package/dist/{rls-BAqP4q66.js → rls-Ck_PocSp.js} +32 -8
- package/dist/{rls-DTvYd-lI.js → rls-CxBZb4rZ.js} +478 -534
- package/dist/{role-builder-uWQZ4v84.js → role-builder-C1VJzvcr.js} +3 -3
- package/dist/rolldown-runtime-C3I60J0A.js +4 -0
- package/dist/{rpc-VSl5ylaQ.js → rpc-Bk1bE-ch.js} +1 -1
- package/dist/run-Bz7DXGEv.js +1 -0
- package/dist/{run-ticket-sxFI6LAk.js → run-ticket-B5d_63Ma.js} +22 -1
- package/dist/{run-ticket-DocwaTKn.d.ts → run-ticket-BpNi7BCi.d.ts} +8 -3
- package/dist/{run-CUKuz0A8.js → run-uLD2aWuO.js} +7 -7
- package/dist/{runtime-D3VTAiCA.js → runtime-Vif3_-zf.js} +1 -1
- package/dist/{saas-BCXRuXca.js → saas-Az_Kz-4D.js} +7 -7
- package/dist/{scan-qvOe0hS3.js → scan-DSljDE1Y.js} +1 -1
- package/dist/{schema-yLuBWWsh.js → schema-gIcCx7zM.js} +1 -1
- package/dist/scim/index.d.ts +3 -2
- package/dist/scim/index.js +3 -1
- package/dist/scim-C7JYhySg.js +44 -0
- package/dist/{scim-ClHtpyV3.js → scim-YNieBO1U.js} +3 -537
- package/dist/{scopes-CUPSjo9q.js → scopes-BHsc-wp-.js} +1 -1
- package/dist/server/index.d.ts +11 -11
- package/dist/server/index.js +7 -7
- package/dist/{server-C3WXAHZj.js → server-Bnga6I50.js} +3 -3
- package/dist/{server-types-CKQiCnON.d.ts → server-types-C4gZUEZy.d.ts} +9 -9
- package/dist/{shared-DvvFzYXe.js → shared-C8LjUdVU.js} +1 -1
- package/dist/{shared-BAK8t4Uo.js → shared-fMKOv8hP.js} +44 -38
- package/dist/{sink-4-eQIRWf.d.ts → sink-Cp_Eot9W.d.ts} +4 -4
- package/dist/{skills-B6lVdD_W.js → skills-Dcjw9_1C.js} +5 -5
- package/dist/{skills-CTYwkl44.js → skills-g_3BS9NC.js} +3 -3
- package/dist/{snapshot-B7DkYg6g.js → snapshot-CbGmYiJx.js} +4 -4
- package/dist/{snapshot-cache-DZHQ0KF6.js → snapshot-cache-DT2Kx1F8.js} +1 -1
- package/dist/{snapshot-cache-PXI-vGpY.d.ts → snapshot-cache-Pahd3IYJ.d.ts} +1 -1
- package/dist/solid/index.d.ts +6 -5
- package/dist/solid/index.js +4 -4
- package/dist/solid-start/index.d.ts +6 -6
- package/dist/solid-start/index.js +3 -3
- package/dist/{sources-vs5dGOsS.js → sources-B-zYB4GS.js} +111 -25
- package/dist/{sources-Xc4zkszI.d.ts → sources-DEEuGb8S.d.ts} +63 -14
- package/dist/sources-qoIJQrJv.js +11 -0
- package/dist/{sql-B5T1IL83.js → sql-B96movIM.js} +5 -1
- package/dist/sql-CDEPCA0g.d.ts +10 -0
- package/dist/{src-Cu2m75uo.js → src-DLFrtrrC.js} +140 -48
- package/dist/ssf/index.d.ts +4 -4
- package/dist/ssf/index.js +640 -211
- package/dist/{standard-webhooks-rbD7LS6h.d.ts → standard-webhooks-B_qRg0ws.d.ts} +2 -2
- package/dist/{store-BDutJkDL.js → store-DSP9oGvh.js} +15 -7
- package/dist/{store-options-u0ycEe8s.js → store-options-B_poyYvg.js} +6 -6
- package/dist/{stores-BUGNbJTt.d.ts → stores-BAZwSYZA.d.ts} +6 -5
- package/dist/{stores-CGhM8BN_.js → stores-BqEuHC05.js} +2 -2
- package/dist/{stream-CeCMJmBB.d.ts → stream-2Vwhl7Er.d.ts} +2 -2
- package/dist/{style-Uao76wO7.js → style-Cqo7-73x.js} +1 -0
- package/dist/{style-DiKWnAvn.js → style-Ds1gNm77.js} +1 -1
- package/dist/supabase/index.d.ts +8 -7
- package/dist/supabase/index.js +5 -5
- package/dist/supabase/middleware.d.ts +6 -6
- package/dist/supabase/middleware.js +6 -6
- package/dist/{supabase-x5JtR2tc.js → supabase--0aJdLEd.js} +8 -6
- package/dist/{supabase-By_frk3U.js → supabase-Bqp7iIY2.js} +3 -3
- package/dist/supabase-hook-DJxC4PoP.js +1 -0
- package/dist/{supabase-hook-BMoEhZir.js → supabase-hook-Dg0SrQWR.js} +1060 -119
- package/dist/supautils-MESd81bF.js +689 -0
- package/dist/{support-session-C6xJQYut.js → support-session-Cna49-3f.js} +2 -2
- package/dist/svelte/index.d.ts +5 -5
- package/dist/svelte/index.js +5 -5
- package/dist/svelte/runtime.d.ts +3 -3
- package/dist/svelte/runtime.js +3 -3
- package/dist/svelte/source.d.ts +5 -5
- package/dist/svelte/source.js +2 -2
- package/dist/sveltekit/index.d.ts +6 -6
- package/dist/sveltekit/index.js +2 -2
- package/dist/tanstack-start/index.d.ts +6 -6
- package/dist/tanstack-start/index.js +2 -2
- package/dist/{tenancy-DN2QAx9z.js → tenancy-CmeBQ6Nv.js} +12 -3
- package/dist/terminal/index.d.ts +6 -6
- package/dist/terminal/index.js +3 -3
- package/dist/testing/core.d.ts +2 -0
- package/dist/testing/core.js +1 -0
- package/dist/testing/fixtures/index.d.ts +12 -0
- package/dist/testing/fixtures/index.js +770 -0
- package/dist/testing/index.d.ts +41 -174
- package/dist/testing/index.js +241 -731
- package/dist/testing/saas/index.d.ts +5 -5
- package/dist/testing/saas/index.js +2 -2
- package/dist/testing/saas/permissions.d.ts +1 -1
- package/dist/testing/saas/permissions.js +1 -1
- package/dist/text-diff-DhITNCnD.js +444 -0
- package/dist/{token-BLZcjQgt.js → token-2qYDvXz2.js} +1 -1
- package/dist/{tool-refusal-CMLN5xhn.js → tool-refusal-reAhIzdl.js} +1 -1
- package/dist/trpc/index.d.ts +6 -6
- package/dist/trpc/index.js +3 -3
- package/dist/{types-Dfv0aTER.d.ts → types-BWZybqs9.d.ts} +110 -8
- package/dist/{types-Druu9wLM.d.ts → types-BXMYB7BY.d.ts} +2 -2
- package/dist/{types-BzzaHxt3.d.ts → types-BfVmNJO5.d.ts} +14 -3
- package/dist/{types-lUlG3TVd.d.ts → types-CIeZcG8v.d.ts} +2 -2
- package/dist/{types-B3_ooZgZ.d.ts → types-CKV71kKh.d.ts} +4 -4
- package/dist/{types-CX7AvRqP.d.ts → types-CQVEQawv.d.ts} +4 -4
- package/dist/{types-C863OQiR.d.ts → types-CeIscIKp.d.ts} +4 -4
- package/dist/{types-C_IiEs64.d.ts → types-CfiKC5aN.d.ts} +1 -1
- package/dist/{types-_WhUP7ud.d.ts → types-D3A19w4A.d.ts} +8 -4
- package/dist/{types-8RnwUAvE.d.ts → types-DBHftjgS.d.ts} +15 -8
- package/dist/{types-BdtSDbBO.d.ts → types-DC44-hqb.d.ts} +2 -2
- package/dist/{types-CVZSNocA.d.ts → types-DXmwu78Z.d.ts} +4 -4
- package/dist/{types-C791HFu9.d.ts → types-cTos-H7p.d.ts} +2 -2
- package/dist/{types-BtHtcKMr.d.ts → types-g7Oy6Zg4.d.ts} +3 -3
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +2 -2
- package/dist/{usage-SveTRTyr.js → usage-Bybmck7f.js} +7 -7
- package/dist/{usage-CRVl9V3t.js → usage-CDaLXCfL.js} +2 -2
- package/dist/usage-y-kBNdqs.js +1 -0
- package/dist/{validation-error-DHUrAkku.d.ts → validation-error-CQpoquwj.d.ts} +1 -1
- package/dist/{verify-BaAKx7t6.js → verify-pe8rL_Qw.js} +350 -83
- package/dist/{views-DTIIO-oQ.js → views-BB8E9dTP.js} +1 -1
- package/dist/vocabulary-Bg3eeT2H.d.ts +67 -0
- package/dist/vue/index.d.ts +6 -5
- package/dist/vue/index.js +4 -4
- package/dist/{waiter-B986w1oK.d.ts → waiter-DwEyouan.d.ts} +2 -2
- package/dist/watch-Bd9mx3rd.js +1 -0
- package/dist/{watch-vea54PRw.js → watch-C-4VmFlL.js} +3 -3
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +2 -2
- package/dist/{where-scope-C9vfEphl.js → where-scope-wq2coopv.js} +41 -21
- package/dist/{who-can-DWBAcaCQ.js → who-can-BwNsI_Js.js} +6 -6
- package/dist/workflow/index.d.ts +3 -3
- package/dist/workflow/index.js +5 -5
- package/dist/workflow-sdk/index.d.ts +5 -5
- package/dist/{wrap-C5jC6sKI.d.ts → wrap-B6R0cMRL.d.ts} +2 -2
- package/dist/{wrap-gX61EtpB.js → wrap-CiSc_KCq.js} +8 -2
- package/package.json +22 -11
- package/schemas/cli-result-v1.json +1 -0
- package/schemas/cloud-api-v1.json +152 -7
- package/schemas/connector-v1.json +8 -0
- package/schemas/supabase-claims-v1.json +5 -0
- package/schemas/supabase-manifest-v1.json +276 -75
- package/skills/permdock/SKILL.md +1 -0
- package/skills/permdock-agents/references/runtimes.md +3 -1
- package/skills/permdock-approvals/references/stores-and-resume.md +2 -1
- package/skills/permdock-data/references/queries-and-rls.md +4 -1
- package/skills/permdock-tenancy/SKILL.md +1 -1
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +1 -0
- package/skills/permdock-wire/references/adapters.md +14 -0
- package/skills/permdock-wire/references/policy-options.md +1 -0
- package/dist/catalog-BoWglDPI.js +0 -1
- package/dist/collect-D7eQDffE.js +0 -1
- package/dist/index-Cp_ilKi1.d.ts +0 -91
- package/dist/load-BxEtwlBS.js +0 -1
- package/dist/output-files-B3IZJksC.js +0 -1
- package/dist/parse-Cxq0WxrA.js +0 -53
- package/dist/peer-C8swJ2GH.js +0 -50
- package/dist/powersync-BkWWCS1K.js +0 -538
- package/dist/powersync-MMJ2mI2q.js +0 -1
- package/dist/prompt-DfZLJLIn.js +0 -32
- package/dist/pull-CvpINcMK.js +0 -1
- package/dist/run-DDwUaJDU.js +0 -1
- package/dist/supabase-hook-CCw5m35C.js +0 -1
- package/dist/supautils-B6HYL9eL.js +0 -91
- package/dist/text-diff-BQcUapOb.js +0 -15
- package/dist/usage-CZpSQP6L.js +0 -1
- package/dist/watch--ovlPOc0.js +0 -1
|
@@ -1,33 +1,37 @@
|
|
|
1
1
|
import { a as sole, r as isRecord } from "./compact-CAf557ZB.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { c as listPermissions, h as formerKeys, n as findPermission } from "./permission-registry-
|
|
2
|
+
import { l as isConditionRef, o as hasConditionOp } from "./refs-CNJBiTFo.js";
|
|
3
|
+
import { A as memoryCustomRoleSource, b as memoryRelationSource } from "./src-DLFrtrrC.js";
|
|
4
|
+
import { c as listPermissions, h as formerKeys, n as findPermission } from "./permission-registry-DpPtSKNZ.js";
|
|
5
5
|
import { a as expandRelation, c as isEdgeRelation, d as isSelfParented, l as isFieldRelation, o as groupEntries, p as restrictedFor, u as isPrincipalRelation } from "./resource-relations-D_jw0BFx.js";
|
|
6
6
|
import { f as nowMillis, u as isoAt } from "./time-DkWjdL98.js";
|
|
7
|
-
import { t as canonicalJson } from "./canonical-json-
|
|
8
|
-
import {
|
|
9
|
-
import { o as requiresStandIn } from "./delegation-
|
|
10
|
-
import { o as flattenGrantee } from "./grantee-
|
|
11
|
-
import { r as relationCondition, t as inheritCondition } from "./grantee-conditions-
|
|
12
|
-
import { p as scopeField } from "./tenancy-
|
|
13
|
-
import {
|
|
7
|
+
import { t as canonicalJson } from "./canonical-json-DwilZ1iB.js";
|
|
8
|
+
import { f as scopeList, l as rootScope, o as normalizeMemberships } from "./scopes-BHsc-wp-.js";
|
|
9
|
+
import { o as requiresStandIn } from "./delegation-C5rOdTp5.js";
|
|
10
|
+
import { o as flattenGrantee } from "./grantee-BrRANrpT.js";
|
|
11
|
+
import { r as relationCondition, t as inheritCondition } from "./grantee-conditions-C6P1Tvol.js";
|
|
12
|
+
import { p as scopeField } from "./tenancy-CmeBQ6Nv.js";
|
|
13
|
+
import { c as policyLevels } from "./policy-DaJy4nK4.js";
|
|
14
14
|
import { t as requiresApproval } from "./approval-required-TzNB9oQA.js";
|
|
15
|
-
import { c as andWhere, m as customRoleScope, v as leveled, y as membershipsClaim } from "./evaluate-
|
|
16
|
-
import { t as getPermDock } from "./permdock-
|
|
17
|
-
import { a as describeError, i as configError, u as usageResult } from "./errors-
|
|
15
|
+
import { c as andWhere, m as customRoleScope, v as leveled, y as membershipsClaim } from "./evaluate-B_oqFbWC.js";
|
|
16
|
+
import { t as getPermDock } from "./permdock-BfWEeMqk.js";
|
|
17
|
+
import { a as describeError, i as configError, u as usageResult } from "./errors-DpP_U6Bf.js";
|
|
18
18
|
import { t as policyRowConditionKeys } from "./row-conditions-h2HythXQ.js";
|
|
19
|
-
import { s as
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
19
|
+
import { s as writeFileEnsuringDir } from "./files-D_JIzUrP.js";
|
|
20
|
+
import { c as jsonSchemaOf } from "./catalog-doc-CV9s6B1S.js";
|
|
21
|
+
import { o as loadPolicyAt } from "./load-DxroEzH1.js";
|
|
22
|
+
import { i as qualifySqlTable, n as defaultSqlCommand, t as SQL_IDENT } from "./sql-B96movIM.js";
|
|
23
|
+
import { c as relationArmSql, i as scopeMembershipTable, r as scopeColumn, u as restrictedRowsSql } from "./compile-BAEO5bOk.js";
|
|
22
24
|
import "./budget-NbEtbJC_.js";
|
|
23
|
-
import
|
|
24
|
-
import "./sources-vs5dGOsS.js";
|
|
25
|
+
import "./sources-B-zYB4GS.js";
|
|
25
26
|
import { t as beforeFirst } from "./text-BMFRfBqc.js";
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
27
|
+
import { t as DOCS_BASE } from "./manifest-Dv6q4i5s.js";
|
|
28
|
+
import { n as fixtureSubject, r as loadFixtures, t as fixtureRow } from "./fixtures-BTMW6zgU.js";
|
|
29
|
+
import { A as quoteQualified, D as qualified, E as helperSchema, N as arrayColumnsOf, O as quoteIdent, P as columnTypesOf, _ as subjectClaimJsonSql, a as customRolesOn, j as quoteTable, k as quoteLiteral, r as HELPERS, v as subjectClaimSql } from "./shared-fMKOv8hP.js";
|
|
30
|
+
import { B as sqlFunctionNames, F as collectGrants, H as graphHelper, N as breakGlassHolder, P as breakGlassKey, R as compileConditionSql, U as graphSqlName, V as CLOSURE, W as graphSqlText, at as activeUserSql, et as permittedIdsHelper, h as accessSql, k as apiKeyAllowsCall, m as requiresSql, q as linkHelper, rt as scopeTable, tt as restrictedHelper, x as capabilityAccessSql, y as tokenScopeAllowsCall, z as contextRefs } from "./ownership-Z2LpxsGa.js";
|
|
31
|
+
import { t as membershipSourcesOf } from "./sources-qoIJQrJv.js";
|
|
32
|
+
import { d as connectPgPool, f as pgConnector, l as validitySql, s as andSql, u as wrapSql } from "./policies-B2lZIurj.js";
|
|
33
|
+
import { s as viewName, t as FIELD_VIEWS } from "./fields-ClcQLrVc.js";
|
|
34
|
+
import { resolve } from "node:path";
|
|
31
35
|
import { createHash, randomUUID } from "node:crypto";
|
|
32
36
|
//#region src/cli/rls/indexes.ts
|
|
33
37
|
/** Postgres truncates identifiers longer than this, in bytes. */
|
|
@@ -279,7 +283,7 @@ function capabilityAccess(item, policy, ctx) {
|
|
|
279
283
|
if (hops.length === 0) return;
|
|
280
284
|
return hops.length === 1 ? hops[0] : hops.map(wrapSql).join(" or ");
|
|
281
285
|
}
|
|
282
|
-
function prepare(item, tables, warnings, skipClosures,
|
|
286
|
+
function prepare(item, tables, warnings, skipClosures, sql, rowActions = false) {
|
|
283
287
|
const { grant, label } = item;
|
|
284
288
|
if (grant.breakGlass !== void 0) {
|
|
285
289
|
warnings.push(`break-glass grant ${label}/${grant.permission.key} reads through permdock_break_glass_${grant.permission.resource}, not a policy`);
|
|
@@ -304,9 +308,14 @@ function prepare(item, tables, warnings, skipClosures, actions, rowActions = fal
|
|
|
304
308
|
warnings.push(`skipped approval grant ${label}/${grant.permission.key}`);
|
|
305
309
|
return;
|
|
306
310
|
}
|
|
307
|
-
const
|
|
311
|
+
const tableless = sql.tableless?.has(grant.permission.resource) === true;
|
|
312
|
+
const command = tableless ? void 0 : commandFor(grant.permission.action, sql.actions);
|
|
308
313
|
const table = tableFor$1(grant.permission.resource, tables);
|
|
309
314
|
if (command === void 0) {
|
|
315
|
+
if (tableless && item.access.kind !== "role") {
|
|
316
|
+
warnings.push(`skipped ${label}/${grant.permission.key}: rls.tables.${grant.permission.resource} is false, so the resource has no table, and only role grants are seeded`);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
310
319
|
if (item.access.kind !== "role" && rowActions) return {
|
|
311
320
|
item,
|
|
312
321
|
command: "none",
|
|
@@ -384,19 +393,19 @@ function groupOf(permission, list) {
|
|
|
384
393
|
return names[0];
|
|
385
394
|
}
|
|
386
395
|
/**
|
|
387
|
-
* One entry per level for every allow of
|
|
388
|
-
*
|
|
389
|
-
*
|
|
396
|
+
* One entry per level for every allow of a role on a resource with levels,
|
|
397
|
+
* keyed `<grant key>@<level>`, whether or not the role is assignable: every
|
|
398
|
+
* role holds the same leveled keys, and `permdock_ceiling` keeps only the
|
|
399
|
+
* assignable roles'. Only `permdock_custom_keys` hands these keys out, to a
|
|
400
|
+
* custom role whose stored allow names the level.
|
|
390
401
|
*/
|
|
391
402
|
function levelEntries(policy, ctx, keys, tables, warnings, skipClosures) {
|
|
392
|
-
|
|
393
|
-
if (custom?.levels !== true) return [];
|
|
394
|
-
const assignable = new Set(custom.assignable);
|
|
403
|
+
if (ctx.customRoles?.levels !== true) return [];
|
|
395
404
|
const out = [];
|
|
396
405
|
for (const [entry, grantKey] of Array.from(keys)) {
|
|
397
406
|
const { item } = entry;
|
|
398
407
|
const { grant } = item;
|
|
399
|
-
if (item.access.kind !== "role" || grant.effect !== "allow" || grant.permission.kind !== "instance"
|
|
408
|
+
if (item.access.kind !== "role" || grant.effect !== "allow" || grant.permission.kind !== "instance") continue;
|
|
400
409
|
if (grant.permission.key.includes("@")) throw configError(`PermDock CLI: permission key '${grant.permission.key}' holds '@', which separates a level in custom-role grant keys`);
|
|
401
410
|
for (const [level, condition] of policyLevels(policy, grant.permission.resource)) {
|
|
402
411
|
const where = andWhere(item.where, condition);
|
|
@@ -404,7 +413,7 @@ function levelEntries(policy, ctx, keys, tables, warnings, skipClosures) {
|
|
|
404
413
|
...item,
|
|
405
414
|
grant: leveled(grant, level, condition),
|
|
406
415
|
...where === void 0 ? {} : { where }
|
|
407
|
-
}, tables, warnings, skipClosures, ctx
|
|
416
|
+
}, tables, warnings, skipClosures, ctx);
|
|
408
417
|
if (prepared !== void 0) {
|
|
409
418
|
keys.set(prepared, `${grantKey}@${level}`);
|
|
410
419
|
out.push(prepared);
|
|
@@ -432,10 +441,10 @@ const PERMANENT_USER = `((select auth.jwt()) ->> 'is_anonymous') is distinct fro
|
|
|
432
441
|
function compileGrants(policy, ctx, tables, warnings, skipClosures, rowActions = false) {
|
|
433
442
|
const items = collectGrants(policy);
|
|
434
443
|
const entries = items.flatMap((item) => {
|
|
435
|
-
const entry = prepare(item, tables, warnings, skipClosures, ctx
|
|
444
|
+
const entry = prepare(item, tables, warnings, skipClosures, ctx, rowActions);
|
|
436
445
|
return entry === void 0 ? [] : [entry];
|
|
437
446
|
});
|
|
438
|
-
const keys = assignKeys(entries, ctx.fields
|
|
447
|
+
const keys = assignKeys(entries, ctx.fields !== void 0);
|
|
439
448
|
entries.push(...levelEntries(policy, ctx, keys, tables, warnings, skipClosures));
|
|
440
449
|
const conditionedKeys = /* @__PURE__ */ new Set();
|
|
441
450
|
for (const [entry, grantKey] of keys) if (entry.using !== void 0 || entry.check !== void 0 || entry.item.grant.validity !== void 0 || entry.item.grant.requires !== void 0) conditionedKeys.add(grantKey);
|
|
@@ -492,7 +501,7 @@ function compileGrants(policy, ctx, tables, warnings, skipClosures, rowActions =
|
|
|
492
501
|
};
|
|
493
502
|
rows.set(`${row.role}\u0000${row.grantKey}\u0000${row.scope}`, row);
|
|
494
503
|
}
|
|
495
|
-
if (!rowActions) continue;
|
|
504
|
+
if (!rowActions || ctx.tableless?.has(grant.permission.resource)) continue;
|
|
496
505
|
}
|
|
497
506
|
const actionOnly = entry.command === "none";
|
|
498
507
|
const command = actionOnly ? "select" : entry.command;
|
|
@@ -506,8 +515,10 @@ function compileGrants(policy, ctx, tables, warnings, skipClosures, rowActions =
|
|
|
506
515
|
const grantKey = keys.get(entry);
|
|
507
516
|
let accessExpr;
|
|
508
517
|
if (access.kind === "role" && grantKey !== void 0) {
|
|
509
|
-
const
|
|
510
|
-
|
|
518
|
+
const via = ctx.vias?.[grant.permission.resource];
|
|
519
|
+
if (via !== void 0 && access.scope !== "global" && access.scope !== via.scope) throw configError(`PermDock CLI: ${label}/${grant.permission.key} is granted at scope ${access.scope}, but rls.tables.${grant.permission.resource}.via reaches ${via.scope}; map the scope with via.scope or grant at ${via.scope}`);
|
|
520
|
+
const column = access.scope === "global" ? void 0 : via?.key ?? scopeField(policy.resources.get(grant.permission.resource), access.scope, policy.scopes);
|
|
521
|
+
accessExpr = accessSql(ctx, access.scope, grantKey, column, access.scope === "global" ? void 0 : via?.helper);
|
|
511
522
|
if (column !== void 0) filters(grant.permission.resource, table, column);
|
|
512
523
|
const row = {
|
|
513
524
|
role: access.role,
|
|
@@ -529,8 +540,10 @@ function compileGrants(policy, ctx, tables, warnings, skipClosures, rowActions =
|
|
|
529
540
|
accessExpr = andSql(accessExpr, validity);
|
|
530
541
|
for (const key of grant.requires ?? []) accessExpr = andSql(accessExpr, requiresSql(ctx, key, policy.resources.get(grant.permission.resource)));
|
|
531
542
|
if (ctx.anonymousSignIns === "deny" && access.kind !== "anyone") accessExpr = andSql(accessExpr, PERMANENT_USER);
|
|
532
|
-
if (ctx.apiKeys !== void 0 && grant.effect === "allow" && access.kind !== "anyone" && access.kind !== "role") {
|
|
533
|
-
const
|
|
543
|
+
if ((ctx.apiKeys !== void 0 || ctx.tokenScopes !== void 0) && grant.effect === "allow" && access.kind !== "anyone" && access.kind !== "role") {
|
|
544
|
+
const covered = requiresStandIn(grant) ? grant.requires ?? [] : [grant.permission.key, ...grant.requires ?? []];
|
|
545
|
+
const ceilings = [...ctx.apiKeys === void 0 ? [] : [apiKeyAllowsCall], ...ctx.tokenScopes === void 0 ? [] : [tokenScopeAllowsCall]];
|
|
546
|
+
const calls = covered.flatMap((key) => ceilings.map((call) => `(select ${call(ctx, quoteLiteral(key))})`));
|
|
534
547
|
accessExpr = andSql(accessExpr, calls.length === 1 ? calls[0] ?? "" : `(${calls.join(" and ")})`);
|
|
535
548
|
}
|
|
536
549
|
const using = compileOptional(entry.using, rowCtx);
|
|
@@ -697,6 +710,229 @@ function rowConditionMessage(policy, keys) {
|
|
|
697
710
|
}
|
|
698
711
|
const ROW_CONDITION_FIX = "call permitted_<scope>_ids_by_permission('<key>') or permdock_has_permission('<key>'), which count only the allows without a row condition (a relationship grant never reaches a topic or a folder), or use a permission whose catalog entry has rowConditions: false";
|
|
699
712
|
//#endregion
|
|
713
|
+
//#region src/cli/sql/tables.ts
|
|
714
|
+
/** The `schema.table` an `rls.tables` entry names, or `resource` when it names none or is `false`. */
|
|
715
|
+
function tableNameOf(entry, resource) {
|
|
716
|
+
if (entry === void 0 || entry === false) return resource;
|
|
717
|
+
return typeof entry === "string" ? entry : entry.table ?? resource;
|
|
718
|
+
}
|
|
719
|
+
/** `rls.tables` as resource to table name, the `via` objects reduced to their `table` and the `false` entries left out. */
|
|
720
|
+
function tableNames(tables) {
|
|
721
|
+
return tables === void 0 ? void 0 : Object.fromEntries(Object.entries(tables).filter(([, entry]) => entry !== false).map(([resource, entry]) => [resource, tableNameOf(entry, resource)]));
|
|
722
|
+
}
|
|
723
|
+
/** The resources `rls.tables` marks `false`: no table, so no policies. */
|
|
724
|
+
function tablelessResources(tables) {
|
|
725
|
+
return new Set(Object.entries(tables ?? {}).filter(([, entry]) => entry === false).map(([resource]) => resource));
|
|
726
|
+
}
|
|
727
|
+
function singular(name) {
|
|
728
|
+
if (name.endsWith("ies")) return `${name.slice(0, -3)}y`;
|
|
729
|
+
if (name.endsWith("sses")) return name.slice(0, -2);
|
|
730
|
+
return name.endsWith("s") && !name.endsWith("ss") ? name.slice(0, -1) : name;
|
|
731
|
+
}
|
|
732
|
+
/** The `via` entries of `rls.tables`, by resource. */
|
|
733
|
+
function resolveVias(tables, scopes, dialect) {
|
|
734
|
+
const out = {};
|
|
735
|
+
for (const [resource, entry] of Object.entries(tables ?? {})) {
|
|
736
|
+
if (typeof entry === "string" || entry === false) continue;
|
|
737
|
+
const { via } = entry;
|
|
738
|
+
const scope = via.scope ?? scopes[0]?.name;
|
|
739
|
+
if (scope === void 0 || !scopes.some((item) => item.name === scope)) throw configError(`PermDock CLI: rls.tables.${resource}.via.scope '${scope ?? ""}' is not a scope of the policy (${scopes.map((item) => item.name).join(", ")})`);
|
|
740
|
+
const name = singular(via.table.slice(via.table.lastIndexOf(".") + 1));
|
|
741
|
+
out[resource] = {
|
|
742
|
+
helper: permittedIdsHelper(name),
|
|
743
|
+
key: via.key ?? `${name}_id`,
|
|
744
|
+
scope,
|
|
745
|
+
table: via.table.includes(".") ? via.table : `public.${via.table}`,
|
|
746
|
+
column: via.column,
|
|
747
|
+
id: via.id ?? "id",
|
|
748
|
+
type: via.type ?? (dialect === "supabase" ? "uuid" : "text")
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
const helpers = /* @__PURE__ */ new Map();
|
|
752
|
+
for (const [resource, via] of Object.entries(out)) {
|
|
753
|
+
const seen = helpers.get(via.helper);
|
|
754
|
+
if (seen !== void 0 && (seen.table !== via.table || seen.column !== via.column || seen.id !== via.id || seen.scope !== via.scope || seen.type !== via.type)) throw configError(`PermDock CLI: rls.tables.${resource}.via names ${via.helper}, which another resource defines over a different parent; give the parents different table names`);
|
|
755
|
+
helpers.set(via.helper, via);
|
|
756
|
+
}
|
|
757
|
+
return Object.keys(out).length === 0 ? void 0 : out;
|
|
758
|
+
}
|
|
759
|
+
//#endregion
|
|
760
|
+
//#region src/cli/rls/verify-findings.ts
|
|
761
|
+
const VERIFY_DOCS = `${DOCS_BASE}/rls#verify`;
|
|
762
|
+
function rlsFinding(code, table, key, message, severity = "error") {
|
|
763
|
+
return {
|
|
764
|
+
code,
|
|
765
|
+
table,
|
|
766
|
+
key,
|
|
767
|
+
message,
|
|
768
|
+
severity
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
/** `<code>:<table>:<key>`: what `--ignore` and `rls.verify.ignore` name. */
|
|
772
|
+
function findingId(finding) {
|
|
773
|
+
return `${finding.code}:${finding.table}:${finding.key}`;
|
|
774
|
+
}
|
|
775
|
+
/** `rls.verify.ignore`, checked, then one entry per `--ignore` id. */
|
|
776
|
+
function verifyIgnores(config, flags = []) {
|
|
777
|
+
const entries = config.rls?.verify?.ignore ?? [];
|
|
778
|
+
if (!Array.isArray(entries)) throw configError("PermDock CLI: rls.verify.ignore must be a list of { id, reason }");
|
|
779
|
+
const out = [];
|
|
780
|
+
for (const [index, entry] of entries.entries()) {
|
|
781
|
+
if (!isRecord(entry) || typeof entry["id"] !== "string" || entry["id"] === "" || typeof entry["reason"] !== "string" || entry["reason"].trim() === "") throw configError(`PermDock CLI: rls.verify.ignore[${String(index)}] needs an id and a non-empty reason`);
|
|
782
|
+
out.push({
|
|
783
|
+
id: entry["id"],
|
|
784
|
+
reason: entry["reason"]
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
for (const id of flags) out.push({
|
|
788
|
+
id,
|
|
789
|
+
reason: "--ignore"
|
|
790
|
+
});
|
|
791
|
+
return out;
|
|
792
|
+
}
|
|
793
|
+
/** Splits `findings` by `ignores`; an id listed twice counts once, with its first reason. */
|
|
794
|
+
function applyIgnores(findings, ignores) {
|
|
795
|
+
const reasons = /* @__PURE__ */ new Map();
|
|
796
|
+
for (const entry of ignores) if (!reasons.has(entry.id)) reasons.set(entry.id, entry.reason);
|
|
797
|
+
const matched = /* @__PURE__ */ new Set();
|
|
798
|
+
const kept = [];
|
|
799
|
+
const ignored = [];
|
|
800
|
+
for (const finding of findings) {
|
|
801
|
+
const id = findingId(finding);
|
|
802
|
+
const reason = reasons.get(id);
|
|
803
|
+
if (reason === void 0) kept.push({
|
|
804
|
+
...finding,
|
|
805
|
+
id
|
|
806
|
+
});
|
|
807
|
+
else {
|
|
808
|
+
matched.add(id);
|
|
809
|
+
ignored.push({
|
|
810
|
+
id,
|
|
811
|
+
reason,
|
|
812
|
+
message: finding.message
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return {
|
|
817
|
+
findings: kept,
|
|
818
|
+
ignored,
|
|
819
|
+
stale: [...reasons].filter(([id]) => !matched.has(id)).map(([id, reason]) => ({
|
|
820
|
+
id,
|
|
821
|
+
reason
|
|
822
|
+
}))
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
function prefixOf(severity) {
|
|
826
|
+
switch (severity) {
|
|
827
|
+
case "error": return "";
|
|
828
|
+
case "warning": return "warning: ";
|
|
829
|
+
case "info": return "info: ";
|
|
830
|
+
default: return severity;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* The `rls verify` result: exit 1 when an error finding is left after the
|
|
835
|
+
* ignores. Text lines carry each finding's id; `--json` data is
|
|
836
|
+
* `{ findings, ignored, stale }`; `--format github|sarif` renders `findings`.
|
|
837
|
+
*/
|
|
838
|
+
function verifyResult(input) {
|
|
839
|
+
const report = applyIgnores(input.findings, input.ignore);
|
|
840
|
+
const failed = report.findings.some((item) => item.severity === "error");
|
|
841
|
+
const lines = [
|
|
842
|
+
...report.findings.map((item) => `${prefixOf(item.severity)}${item.message} [${item.id}]`),
|
|
843
|
+
...report.ignored.map((item) => `ignored ${item.id}: ${item.reason}`),
|
|
844
|
+
...report.stale.map((item) => `warning: ignore entry ${item.id} matches no finding; remove it from rls.verify.ignore`),
|
|
845
|
+
...failed ? [] : [input.summary],
|
|
846
|
+
...input.notes ?? []
|
|
847
|
+
];
|
|
848
|
+
return {
|
|
849
|
+
code: failed ? 1 : 0,
|
|
850
|
+
output: lines.join("\n"),
|
|
851
|
+
data: report,
|
|
852
|
+
findings: [...report.findings.map((item) => ({
|
|
853
|
+
code: item.code,
|
|
854
|
+
id: item.id,
|
|
855
|
+
severity: item.severity,
|
|
856
|
+
message: item.message,
|
|
857
|
+
docs: VERIFY_DOCS
|
|
858
|
+
})), ...report.stale.map((item) => ({
|
|
859
|
+
code: "ignore-stale",
|
|
860
|
+
id: `ignore-stale:${item.id}`,
|
|
861
|
+
severity: "warning",
|
|
862
|
+
message: `ignore entry ${item.id} matches no finding`,
|
|
863
|
+
fix: "remove it from rls.verify.ignore",
|
|
864
|
+
docs: VERIFY_DOCS
|
|
865
|
+
}))]
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
//#endregion
|
|
869
|
+
//#region src/cli/rls/verify-pgtap-memberships.ts
|
|
870
|
+
function insert(table, columns) {
|
|
871
|
+
return `insert into ${quoteTable(table)} (${columns.map(([name]) => quoteIdent(name)).join(", ")}) values (${columns.map(([, value]) => quoteLiteral(value)).join(", ")}) on conflict do nothing;`;
|
|
872
|
+
}
|
|
873
|
+
/** The rows of a table mapped by `rls.memberships`: one per role, for a plain role column. */
|
|
874
|
+
function mappedRows(config, scopes, user, membership) {
|
|
875
|
+
const { scope, id } = membership;
|
|
876
|
+
if (scope === void 0 || id === void 0) return [];
|
|
877
|
+
const table = scopeMembershipTable(config.rls?.memberships, scopes, scope);
|
|
878
|
+
const idColumn = table === void 0 ? void 0 : scopeColumn(table, scopes, scope);
|
|
879
|
+
const roleColumn = table?.role;
|
|
880
|
+
if (table === void 0 || idColumn === void 0 || typeof roleColumn !== "string") return [];
|
|
881
|
+
const rootName = rootScope(scopes);
|
|
882
|
+
const tenantColumn = rootName === void 0 || rootName === scope ? void 0 : scopeColumn(table, scopes, rootName);
|
|
883
|
+
const tenant = rootName === void 0 ? void 0 : membership.within?.[rootName];
|
|
884
|
+
const extra = [];
|
|
885
|
+
for (const [name, value] of Object.entries(membership.within ?? {})) {
|
|
886
|
+
const column = scopeColumn(table, scopes, name);
|
|
887
|
+
if (column !== void 0 && name !== scope) extra.push([column, value]);
|
|
888
|
+
}
|
|
889
|
+
if (tenantColumn !== void 0 && tenant !== void 0 && !extra.some(([column]) => column === tenantColumn)) extra.push([tenantColumn, tenant]);
|
|
890
|
+
return membership.roles.map((role) => insert(table.table, [
|
|
891
|
+
[table.user, user],
|
|
892
|
+
[idColumn, id],
|
|
893
|
+
...extra,
|
|
894
|
+
[roleColumn, role]
|
|
895
|
+
]));
|
|
896
|
+
}
|
|
897
|
+
/** The rows of a `membershipSources` source: only a source whose columns are plain, never a `through` or `map`. */
|
|
898
|
+
function sourceRows(source, scopes, user, membership) {
|
|
899
|
+
const { scope, id } = membership;
|
|
900
|
+
if (scope === void 0 || id === void 0) return [];
|
|
901
|
+
const names = new Set(scopes.map((entry) => entry.name));
|
|
902
|
+
if ("through" in source.user || !names.has(scope) || "value" in source.scope && source.scope.value !== scope) return [];
|
|
903
|
+
const role = source.role;
|
|
904
|
+
const roleColumns = Array.isArray(role) || !("column" in role) || "through" in role ? void 0 : "map" in role ? void 0 : role.column;
|
|
905
|
+
const fixed = "value" in source.scope ? [] : [[source.scope.column, scope]];
|
|
906
|
+
const within = source.within;
|
|
907
|
+
const ancestors = within === void 0 || !("columns" in within) ? [] : Object.entries(within.columns).flatMap(([name, column]) => {
|
|
908
|
+
const value = membership.within?.[name];
|
|
909
|
+
return value === void 0 ? [] : [[column, value]];
|
|
910
|
+
});
|
|
911
|
+
const base = [
|
|
912
|
+
[source.user.column, user],
|
|
913
|
+
[source.id.column, id],
|
|
914
|
+
...fixed,
|
|
915
|
+
...ancestors
|
|
916
|
+
];
|
|
917
|
+
if (roleColumns === void 0) return [];
|
|
918
|
+
return membership.roles.map((value) => insert(source.table, [...base, [roleColumns, value]]));
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* The statements that put a fixture's memberships into the configured
|
|
922
|
+
* membership tables and sources, so the helpers of `authorize: 'database'`
|
|
923
|
+
* find them. A membership no plain table or source can hold (a `through`
|
|
924
|
+
* or mapped role column, an `on` membership) is listed in a comment.
|
|
925
|
+
*/
|
|
926
|
+
function fixtureMembershipSql(config, scopes, fixture, sources) {
|
|
927
|
+
const lines = [];
|
|
928
|
+
for (const membership of normalizeMemberships(fixture.subject.memberships ?? [], scopes)) {
|
|
929
|
+
const rows = [...mappedRows(config, scopes, fixture.subject.id, membership), ...sources.flatMap((source) => sourceRows(source, scopes, fixture.subject.id, membership))];
|
|
930
|
+
if (rows.length === 0) lines.push(`-- no membership table holds ${membership.scope ?? "on"} ${membership.id ?? ""}: not seeded`);
|
|
931
|
+
lines.push(...rows);
|
|
932
|
+
}
|
|
933
|
+
return lines;
|
|
934
|
+
}
|
|
935
|
+
//#endregion
|
|
700
936
|
//#region src/cli/rls/verify-pgtap.ts
|
|
701
937
|
/** A custom role's scope and pinned instance, as the `custom_role_*` tables store them. */
|
|
702
938
|
function customRoleAt(role, scopes) {
|
|
@@ -789,7 +1025,16 @@ function subjectSettings(fixture, subject) {
|
|
|
789
1025
|
const roles = fixture.subject.roles ?? [];
|
|
790
1026
|
const memberships = membershipsClaim(fixture.subject.memberships ?? [], subject.customRoles, subject.scopes);
|
|
791
1027
|
if (subject.dialect === "guc") {
|
|
1028
|
+
const fixed = /* @__PURE__ */ new Set([
|
|
1029
|
+
"user_id",
|
|
1030
|
+
"sub",
|
|
1031
|
+
"role",
|
|
1032
|
+
"memberships",
|
|
1033
|
+
subject.roleClaim,
|
|
1034
|
+
subject.tenantClaim
|
|
1035
|
+
]);
|
|
792
1036
|
const settings = [
|
|
1037
|
+
...Object.entries(fixture.subject.claims ?? {}).flatMap(([name, value]) => fixed.has(name) || !SQL_IDENT.test(name) ? [] : [[`${subject.gucPrefix}.${name}`, typeof value === "string" ? value : JSON.stringify(value)]]),
|
|
793
1038
|
[`${subject.gucPrefix}.user_id`, fixture.subject.id],
|
|
794
1039
|
[`${subject.gucPrefix}.${subject.roleClaim}`, roles.join(",")],
|
|
795
1040
|
[`${subject.gucPrefix}.memberships`, JSON.stringify(memberships)]
|
|
@@ -839,9 +1084,12 @@ function customRoleRows(schema, customRoles, scopes) {
|
|
|
839
1084
|
}
|
|
840
1085
|
return rows;
|
|
841
1086
|
}
|
|
842
|
-
function
|
|
1087
|
+
function databaseMode(config) {
|
|
843
1088
|
const rls = config.rls;
|
|
844
|
-
return
|
|
1089
|
+
return (rls?.authorize ?? rls?.rbac?.authorize ?? "jwt") === "database";
|
|
1090
|
+
}
|
|
1091
|
+
function seedsCustomRoleTables(config) {
|
|
1092
|
+
return customRolesOn(config.rls) && databaseMode(config);
|
|
845
1093
|
}
|
|
846
1094
|
function customRoleSchema(config) {
|
|
847
1095
|
return config.rls?.schema ?? config.rls?.rbac?.schema ?? "permdock";
|
|
@@ -859,6 +1107,7 @@ function emitPgtap(input) {
|
|
|
859
1107
|
PGTAP_DECISION_SQL
|
|
860
1108
|
];
|
|
861
1109
|
if (seedsCustomRoleTables(input.config)) for (const row of customRoleRows(customRoleSchema(input.config), input.customRoles, scopes)) lines.push(`${inlineStatement(row)};`);
|
|
1110
|
+
const sources = membershipSourcesOf(input.config).map((source) => source.sql.manifest);
|
|
862
1111
|
for (const [index, fixture] of input.fixtures.entries()) {
|
|
863
1112
|
const permission = findPermission(input.policy.permissions, fixture.action);
|
|
864
1113
|
const status = input.inProcess[index];
|
|
@@ -868,10 +1117,14 @@ function emitPgtap(input) {
|
|
|
868
1117
|
lines.push(`select skip(${quoteLiteral(`${label}: opaque grant untestable app-side`)}, 1);`);
|
|
869
1118
|
continue;
|
|
870
1119
|
}
|
|
1120
|
+
if (input.config.rls?.tables?.[permission.resource] === false) {
|
|
1121
|
+
lines.push(`select skip(${quoteLiteral(`${label}: rls.tables.${permission.resource} is false, so there is no table`)}, 1);`);
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
871
1124
|
const outcome = status.granted ? "granted" : "denied";
|
|
872
|
-
const table = input.config.rls?.tables?.[permission.resource]
|
|
1125
|
+
const table = tableNameOf(input.config.rls?.tables?.[permission.resource], permission.resource);
|
|
873
1126
|
const statement = inlineStatement(statementFor(fixture, permission.action, table));
|
|
874
|
-
lines.push(`-- ${label}`, "savepoint permdock_fixture;", "set local role \"authenticated\";", ...subjectSettings(fixture, subject).map(([name, value]) => `select set_config(${quoteLiteral(name)}, ${quoteLiteral(value)}, true);`), `select is(${PGTAP_DECISION}(${quoteLiteral(statement)}), ${quoteLiteral(outcome)}, ${quoteLiteral(`${label} ${outcome}`)});`, "rollback to savepoint permdock_fixture;");
|
|
1127
|
+
lines.push(`-- ${label}`, "savepoint permdock_fixture;", ...databaseMode(input.config) ? fixtureMembershipSql(input.config, scopes, fixture, sources) : [], "set local role \"authenticated\";", ...subjectSettings(fixture, subject).map(([name, value]) => `select set_config(${quoteLiteral(name)}, ${quoteLiteral(value)}, true);`), `select is(${PGTAP_DECISION}(${quoteLiteral(statement)}), ${quoteLiteral(outcome)}, ${quoteLiteral(`${label} ${outcome}`)});`, "rollback to savepoint permdock_fixture;");
|
|
875
1128
|
}
|
|
876
1129
|
lines.push("select * from finish();", "rollback;");
|
|
877
1130
|
return `${lines.join("\n")}\n`;
|
|
@@ -1399,7 +1652,7 @@ async function seedRows(query, table, rows, given, entropy) {
|
|
|
1399
1652
|
async function verifyTree(input) {
|
|
1400
1653
|
const { policy, query } = input;
|
|
1401
1654
|
const entropy = input.entropy ?? SYSTEM_ENTROPY;
|
|
1402
|
-
const tables = input.config.rls?.tables;
|
|
1655
|
+
const tables = tableNames(input.config.rls?.tables);
|
|
1403
1656
|
const treeValues = input.config.rls?.treeValues ?? {};
|
|
1404
1657
|
const seed = (table, list) => seedRows(query, table, list, treeValues[table] ?? {}, entropy);
|
|
1405
1658
|
const walked = [...graphPlan(policy).values()].filter((entry) => entry.closure !== void 0);
|
|
@@ -1506,7 +1759,7 @@ async function verifyTree(input) {
|
|
|
1506
1759
|
checked += 1;
|
|
1507
1760
|
const id = String(row[node.id]);
|
|
1508
1761
|
const allowed = permdock.can(permission, row);
|
|
1509
|
-
if (allowed !== seen.has(id)) mismatches.push(`${permission.key} ${id}: in-process ${allowed ? "granted" : "denied"}, database ${seen.has(id) ? "allowed" : "filtered"}`);
|
|
1762
|
+
if (allowed !== seen.has(id)) mismatches.push(rlsFinding("tree-mismatch", node.name, permission.key, `${permission.key} ${id}: in-process ${allowed ? "granted" : "denied"}, database ${seen.has(id) ? "allowed" : "filtered"}`));
|
|
1510
1763
|
else if (allowed) granted += 1;
|
|
1511
1764
|
}
|
|
1512
1765
|
}
|
|
@@ -1520,6 +1773,10 @@ async function verifyTree(input) {
|
|
|
1520
1773
|
}
|
|
1521
1774
|
//#endregion
|
|
1522
1775
|
//#region src/cli/rls/verify-database.ts
|
|
1776
|
+
/** A fixture's part of a finding id: its subject and row. */
|
|
1777
|
+
function fixtureKey(fixture) {
|
|
1778
|
+
return `${fixture.subject.id}/${String(rowId(fixture.row))}`;
|
|
1779
|
+
}
|
|
1523
1780
|
/** Columns of `row` that hold a value; a masked column reads as null. */
|
|
1524
1781
|
function valuedColumns(row) {
|
|
1525
1782
|
return isRecord(row) ? Object.keys(row).filter((name) => row[name] !== null && row[name] !== void 0).toSorted() : [];
|
|
@@ -1536,8 +1793,9 @@ function expectedFields(granted, picked, key, row) {
|
|
|
1536
1793
|
}
|
|
1537
1794
|
function fieldsMismatch(fixture, want, seen) {
|
|
1538
1795
|
const kept = want.join(", ");
|
|
1539
|
-
|
|
1540
|
-
|
|
1796
|
+
const mismatch = (message) => rlsFinding("field-mismatch", fixture.action, fixtureKey(fixture), message);
|
|
1797
|
+
if (typeof seen === "string") return [mismatch(`${fixture.action}: field view read failed (${seen}), pick keeps [${kept}]`)];
|
|
1798
|
+
return seen.join(", ") === kept ? [] : [mismatch(`${fixture.action} ${String(rowId(fixture.row))}: field view returns [${seen.join(", ")}], pick keeps [${kept}]`)];
|
|
1541
1799
|
}
|
|
1542
1800
|
/** The field view's columns for the fixture's row: the view, or the table itself when no grant limits its fields. */
|
|
1543
1801
|
async function viewFields(query, table, key) {
|
|
@@ -1590,7 +1848,7 @@ async function verifyAgainstDatabase(input) {
|
|
|
1590
1848
|
const conditioned = policyRowConditionKeys(input.policy);
|
|
1591
1849
|
for (const found of helperTablePoliciesFromRows((await query(HELPER_TABLE_POLICIES_SQL)).rows)) {
|
|
1592
1850
|
const keys = found.keys.filter((key) => conditioned.has(key));
|
|
1593
|
-
if (keys.length > 0) mismatches.push(`PD037 ${rowConditionMessage(found, keys)}`);
|
|
1851
|
+
if (keys.length > 0) mismatches.push(rlsFinding("condition-blind", found.table, found.name, `PD037 ${rowConditionMessage(found, keys)}`));
|
|
1594
1852
|
}
|
|
1595
1853
|
for (const [index, fixture] of input.fixtures.entries()) {
|
|
1596
1854
|
const permission = findPermission(input.policy.permissions, fixture.action);
|
|
@@ -1600,7 +1858,11 @@ async function verifyAgainstDatabase(input) {
|
|
|
1600
1858
|
notes.push(`${fixture.action}: opaque grant untestable app-side`);
|
|
1601
1859
|
continue;
|
|
1602
1860
|
}
|
|
1603
|
-
|
|
1861
|
+
if (input.config.rls?.tables?.[permission.resource] === false) {
|
|
1862
|
+
notes.push(`${fixture.action}: rls.tables.${permission.resource} is false, so there is no table`);
|
|
1863
|
+
continue;
|
|
1864
|
+
}
|
|
1865
|
+
const table = tableNameOf(input.config.rls?.tables?.[permission.resource], permission.resource);
|
|
1604
1866
|
await query("begin");
|
|
1605
1867
|
try {
|
|
1606
1868
|
if (seedsTables) await seedCustomRoles(query, schema, input.customRoles, scopes);
|
|
@@ -1609,7 +1871,7 @@ async function verifyAgainstDatabase(input) {
|
|
|
1609
1871
|
const result = await query(statement.sql, statement.values);
|
|
1610
1872
|
const count = result.rowCount ?? result.rows.length;
|
|
1611
1873
|
const database = result.code === "42501" ? "rejected" : count > 0 ? "allowed" : "filtered";
|
|
1612
|
-
if (!(status.granted ? database === "allowed" : database === "filtered" || database === "rejected")) mismatches.push(`${fixture.action}: in-process ${status.granted ? "granted" : "denied"}, database ${database}`);
|
|
1874
|
+
if (!(status.granted ? database === "allowed" : database === "filtered" || database === "rejected")) mismatches.push(rlsFinding("parity-mismatch", fixture.action, fixtureKey(fixture), `${fixture.action}: in-process ${status.granted ? "granted" : "denied"}, database ${database}`));
|
|
1613
1875
|
else if (status.kind === "sqlFunction") notes.push(`${fixture.action}: verified through twin`);
|
|
1614
1876
|
mismatches.push(...status.fields === void 0 ? [] : fieldsMismatch(fixture, status.fields, await viewFields(query, table, rowId(fixture.row))));
|
|
1615
1877
|
} finally {
|
|
@@ -1625,7 +1887,7 @@ async function verifyAgainstDatabase(input) {
|
|
|
1625
1887
|
};
|
|
1626
1888
|
}
|
|
1627
1889
|
/** `--tree`: seeds a generated object graph in a rolled-back transaction and compares `can()` with RLS. */
|
|
1628
|
-
async function verifyTreeAgainstDatabase(db, policy, config, connect) {
|
|
1890
|
+
async function verifyTreeAgainstDatabase(db, policy, config, connect, ignore) {
|
|
1629
1891
|
const client = await connect(db);
|
|
1630
1892
|
const scopes = scopeList(policy.scopes);
|
|
1631
1893
|
const query = async (sql, values) => {
|
|
@@ -1647,15 +1909,12 @@ async function verifyTreeAgainstDatabase(db, policy, config, connect) {
|
|
|
1647
1909
|
action: "read"
|
|
1648
1910
|
}, subjectOf(config, [], scopes))
|
|
1649
1911
|
});
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
code: 0,
|
|
1657
|
-
output: result.notes.length === 0 ? verified : `${verified}\n${result.notes.join("\n")}`
|
|
1658
|
-
};
|
|
1912
|
+
return verifyResult({
|
|
1913
|
+
findings: result.mismatches,
|
|
1914
|
+
ignore,
|
|
1915
|
+
notes: result.notes,
|
|
1916
|
+
summary: `verified ${String(result.checked)} tree check(s) against the database (${String(result.granted)} granted)`
|
|
1917
|
+
});
|
|
1659
1918
|
} catch (cause) {
|
|
1660
1919
|
return {
|
|
1661
1920
|
code: 2,
|
|
@@ -1668,6 +1927,8 @@ async function verifyTreeAgainstDatabase(db, policy, config, connect) {
|
|
|
1668
1927
|
}
|
|
1669
1928
|
//#endregion
|
|
1670
1929
|
//#region src/cli/rls/verify.ts
|
|
1930
|
+
/** Where `rls verify --kind pgtap` writes, the folder `supabase test db` reads. */
|
|
1931
|
+
const PGTAP_OUT = "supabase/tests/permdock_rls.test.sql";
|
|
1671
1932
|
function canFixture(permdock, permission, row) {
|
|
1672
1933
|
if (permission.kind === "collection") return permdock.can(permission, row);
|
|
1673
1934
|
return permdock.can(permission, row);
|
|
@@ -1679,6 +1940,11 @@ function grantKind(policy, key) {
|
|
|
1679
1940
|
return "portable";
|
|
1680
1941
|
}
|
|
1681
1942
|
async function runRlsVerify(input) {
|
|
1943
|
+
const ignore = input.ignore ?? [];
|
|
1944
|
+
if (input.out !== void 0 && input.format !== "pgtap") return {
|
|
1945
|
+
code: 2,
|
|
1946
|
+
output: "PermDock CLI: rls verify --out needs --kind pgtap"
|
|
1947
|
+
};
|
|
1682
1948
|
const connect = input.connect ?? pgConnector("permdock rls verify --db-url-stdin", input.cwd);
|
|
1683
1949
|
const policyPath = input.from ?? input.config.policy;
|
|
1684
1950
|
if (policyPath === void 0) return {
|
|
@@ -1692,7 +1958,7 @@ async function runRlsVerify(input) {
|
|
|
1692
1958
|
output: "PermDock CLI: rls verify --tree needs a database: --db-url-stdin, DATABASE_URL or SUPABASE_DB_URL"
|
|
1693
1959
|
};
|
|
1694
1960
|
try {
|
|
1695
|
-
return await verifyTreeAgainstDatabase(input.db, policy, input.config, connect);
|
|
1961
|
+
return await verifyTreeAgainstDatabase(input.db, policy, input.config, connect, ignore);
|
|
1696
1962
|
} catch (cause) {
|
|
1697
1963
|
return usageResult(cause);
|
|
1698
1964
|
}
|
|
@@ -1702,11 +1968,11 @@ async function runRlsVerify(input) {
|
|
|
1702
1968
|
const mismatches = [];
|
|
1703
1969
|
const notes = [];
|
|
1704
1970
|
const inProcess = [];
|
|
1705
|
-
const fieldsMode = input.config.rls?.fields
|
|
1971
|
+
const fieldsMode = input.config.rls?.fields !== void 0;
|
|
1706
1972
|
for (const fixture of fixtures) {
|
|
1707
1973
|
const permission = findPermission(policy.permissions, fixture.action);
|
|
1708
1974
|
if (permission === void 0) {
|
|
1709
|
-
mismatches.push(`${fixture.action}: unknown permission`);
|
|
1975
|
+
mismatches.push(rlsFinding("unknown-permission", fixture.action, fixtureKey(fixture), `${fixture.action}: unknown permission`));
|
|
1710
1976
|
inProcess.push({
|
|
1711
1977
|
action: fixture.action,
|
|
1712
1978
|
granted: false,
|
|
@@ -1718,7 +1984,7 @@ async function runRlsVerify(input) {
|
|
|
1718
1984
|
const permdock = await getPermDock(policy, fixtureSubject(fixture.subject), { customRoles: memoryCustomRoleSource(customRoles) });
|
|
1719
1985
|
const granted = canFixture(permdock, permission, fixtureRow(fixture, permission.kind));
|
|
1720
1986
|
const outcome = granted ? "granted" : "denied";
|
|
1721
|
-
if (fixture.expected !== void 0 && fixture.expected !== outcome) mismatches.push(`${fixture.action}: in-process ${outcome}, expected ${fixture.expected}`);
|
|
1987
|
+
if (fixture.expected !== void 0 && fixture.expected !== outcome) mismatches.push(rlsFinding("expected-mismatch", fixture.action, fixtureKey(fixture), `${fixture.action}: in-process ${outcome}, expected ${fixture.expected}`));
|
|
1722
1988
|
if (kind === "opaque") notes.push(`${fixture.action}: opaque grant untestable app-side`);
|
|
1723
1989
|
const fields = fieldsMode && permission.kind === "instance" && commandFor(permission.action, input.config.rls?.actions) === "select" ? expectedFields(granted, permdock.pick(permission, fixture.row), policy.resources.get(permission.resource)?.id ?? "id", fixture.row) : void 0;
|
|
1724
1990
|
inProcess.push({
|
|
@@ -1728,19 +1994,23 @@ async function runRlsVerify(input) {
|
|
|
1728
1994
|
...fields === void 0 ? {} : { fields }
|
|
1729
1995
|
});
|
|
1730
1996
|
}
|
|
1731
|
-
if (input.format === "pgtap")
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1997
|
+
if (input.format === "pgtap") {
|
|
1998
|
+
const outRel = input.out ?? PGTAP_OUT;
|
|
1999
|
+
const result = verifyResult({
|
|
2000
|
+
findings: mismatches,
|
|
2001
|
+
ignore,
|
|
2002
|
+
summary: `wrote ${outRel}`
|
|
2003
|
+
});
|
|
2004
|
+
if (result.code !== 0) return result;
|
|
2005
|
+
writeFileEnsuringDir(resolve(input.cwd, outRel), emitPgtap({
|
|
1737
2006
|
fixtures,
|
|
1738
2007
|
inProcess,
|
|
1739
2008
|
policy,
|
|
1740
2009
|
config: input.config,
|
|
1741
2010
|
customRoles
|
|
1742
|
-
})
|
|
1743
|
-
|
|
2011
|
+
}));
|
|
2012
|
+
return result;
|
|
2013
|
+
}
|
|
1744
2014
|
if (input.db !== void 0) try {
|
|
1745
2015
|
const database = await verifyAgainstDatabase({
|
|
1746
2016
|
db: input.db,
|
|
@@ -1756,15 +2026,12 @@ async function runRlsVerify(input) {
|
|
|
1756
2026
|
} catch (cause) {
|
|
1757
2027
|
return usageResult(cause);
|
|
1758
2028
|
}
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
code: 0,
|
|
1766
|
-
output: notes.length === 0 ? verified : `${verified}\n${notes.join("\n")}`
|
|
1767
|
-
};
|
|
2029
|
+
return verifyResult({
|
|
2030
|
+
findings: mismatches,
|
|
2031
|
+
ignore,
|
|
2032
|
+
notes,
|
|
2033
|
+
summary: `verified ${String(fixtures.length)} fixture(s) ${input.db === void 0 ? "in-process" : "in-process and against the database"}`
|
|
2034
|
+
});
|
|
1768
2035
|
}
|
|
1769
2036
|
//#endregion
|
|
1770
|
-
export { indexesSql as _, graphSql as a,
|
|
2037
|
+
export { INDEX_COLUMNS_SQL as C, readTableIndexFacts as D, pruneIndexTargets as E, COLUMNS_SQL as S, indexesSql as T, helperTablePolicies as _, graphSql as a, compileGrants as b, verifyResult as c, tableNames as d, tablelessResources as f, helperCallKeys as g, callsHelper as h, graphPlan as i, resolveVias as l, ROW_CONDITION_FIX as m, runRlsVerify as n, rlsFinding as o, HELPER_TABLES as p, closureDepths as r, verifyIgnores as s, canFixture as t, tableNameOf as u, rowConditionMessage as v, indexTargets as w, tableFor$1 as x, commandFor as y };
|