permdock 0.6.0 → 0.8.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 +2 -3
- package/dist/{2020-C9VN-x8c.js → 2020-CJbO8Bvz.js} +1 -3
- package/dist/a2a/index.d.ts +7 -7
- package/dist/a2a/index.js +9 -9
- package/dist/action-result-CL-QCITA.d.ts +19 -0
- package/dist/{adapter-context-CRhqyzCk.js → adapter-context-CIQpQxbE.js} +8 -2
- package/dist/{adoption-scan-DCYe60Pi.js → adoption-scan-BLs67P1l.js} +5 -5
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +1 -1
- package/dist/ai-sdk/index.d.ts +31 -10
- package/dist/ai-sdk/index.js +22 -12
- package/dist/{app-data-MuyzVqX8.js → app-data-UNNm07jV.js} +10 -7
- package/dist/{approval-header-CJACtHkv.js → approval-header-8QV6MU4H.js} +6 -3
- package/dist/{approval-prompt-Dg5LeAcT.js → approval-prompt-B8hQY69x.js} +5 -6
- package/dist/{approval-prompt-DvSSPQJX.d.ts → approval-prompt-PYagyQUj.d.ts} +13 -11
- package/dist/approvals/index.d.ts +5 -5
- package/dist/approvals/index.js +7 -7
- package/dist/{approvals-p1PGoVjb.js → approvals-DnIBTuXE.js} +128 -78
- package/dist/{approvers-CqPOLD-o.js → approvers-B4r0N_Uz.js} +8 -2
- package/dist/{arazzo-41WuzHou.js → arazzo-3RhGfkAK.js} +2 -2
- package/dist/{arazzo-CaUVBn7D.js → arazzo-B5-ulWif.js} +6 -6
- package/dist/{args-IIolxxKA.js → args-0FrVs5EH.js} +125 -8
- package/dist/astro/index.d.ts +30 -12
- package/dist/astro/index.js +2 -2
- package/dist/{audience-DabClFLQ.js → audience-BAkBDMqn.js} +8 -8
- package/dist/{auth-DRLEizbL.js → auth--yPyZNgr.js} +21 -10
- package/dist/authzen/index.d.ts +171 -25
- package/dist/authzen/index.js +321 -149
- package/dist/{automation-BNM3TtDo.d.ts → automation-DqEoWHBc.d.ts} +42 -32
- package/dist/{automation-DVjOckjV.js → automation-x3KEUusw.js} +22 -21
- package/dist/base-url-BQbddZO5.js +9 -0
- package/dist/better-auth/index.d.ts +2 -2
- package/dist/better-auth/index.js +13 -7
- package/dist/better-supabase/index.d.ts +392 -220
- package/dist/better-supabase/index.js +293 -152
- package/dist/{bind-DOyrv6zi.d.ts → bind-Cp2hBHzP.d.ts} +27 -12
- package/dist/{bind-CAvgUuKs.js → bind-D_sZSVA3.js} +22 -10
- package/dist/{body-CH5of8Ne.js → body-CJQwYVa8.js} +3 -3
- package/dist/{body-text-QcXok_SI.js → body-text-DQhcmqdY.js} +8 -4
- package/dist/{boundary-BM8a_P6E.js → boundary-BV3O1eud.js} +8 -2
- package/dist/{boundary-BmHxVH1J.d.ts → boundary-C9ck-3uK.d.ts} +13 -4
- package/dist/{budget-NbEtbJC_.js → budget-BELja5A9.js} +6 -3
- package/dist/{budget-JqcRqyhK.d.ts → budget-hYsPz_yE.d.ts} +6 -3
- package/dist/{build-instance-Bu_eDqYw.js → build-instance-CNWaYJyr.js} +3 -3
- package/dist/{canonical-json-BC8_nMTw.js → canonical-json-DwilZ1iB.js} +6 -3
- package/dist/{capability-N1K9IpqS.js → capability-BqX94r07.js} +31 -25
- package/dist/{capability-Drg-1YhL.d.ts → capability-ChuiONQU.d.ts} +41 -29
- package/dist/{casbin-Dyu2gnUD.js → casbin-H0rKxBx2.js} +16 -8
- package/dist/{casbin-Cr7RLtA3.js → casbin-cbSbhT_k.js} +2 -2
- package/dist/{casl-64gpT-8x.js → casl-C4OINZHI.js} +2 -2
- package/dist/{casl-DjEDhTCP.js → casl-CHHoCldP.js} +19 -10
- package/dist/catalog/index.d.ts +104 -23
- package/dist/catalog/index.js +117 -8
- package/dist/{catalog-HD2gf5mY.js → catalog-BFpz2EmT.js} +57 -26
- package/dist/{catalog-DZVbrF-z.js → catalog-DCspHDc8.js} +3 -3
- package/dist/{catalog-doc-Cdn7-0pQ.js → catalog-doc-DjMKzJp9.js} +53 -16
- package/dist/{catalog-fingerprint-CCP0-yQS.js → catalog-fingerprint-e6aCWosn.js} +19 -15
- package/dist/catalog-mRQQQhNK.js +1 -0
- package/dist/catalog-operations-Dadrl7Z-.js +60 -0
- package/dist/{create-CQWIpUG-.js → challenge-DImNcR7B.js} +157 -39
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/index.js +13 -10
- package/dist/chat-sdk/index.d.ts +44 -26
- package/dist/chat-sdk/index.js +17 -17
- package/dist/checks-CGkP3Z7A.js +40 -0
- package/dist/{claims-map-7oQ3ue4X.js → claims-map-CKkoAAYF.js} +5 -2
- package/dist/{claims-map-Ca-QX8C6.d.ts → claims-map-D1Pb5oRq.d.ts} +8 -5
- package/dist/claude-agent/index.d.ts +6 -6
- package/dist/claude-agent/index.js +6 -3
- package/dist/clerk/index.d.ts +11 -7
- package/dist/clerk/index.js +6 -6
- package/dist/cli/bin.js +4 -4
- package/dist/cli/index.d.ts +82 -7
- package/dist/cli/index.js +6 -6
- package/dist/{clients-C7zLFuWT.d.ts → clients-C_NOET3z.d.ts} +5 -5
- package/dist/{clients-DjU5ABMy.js → clients-DYr3U86j.js} +3 -3
- package/dist/cloud/index.d.ts +186 -38
- package/dist/cloud/index.js +293 -31
- package/dist/{cloud-ytQ3uviA.js → cloud-BFlGMaak.js} +5 -6
- package/dist/{codemod-5mpSpnUY.js → codemod-DLQ9Zpo4.js} +3 -4
- package/dist/{codemod-ua2hHy7w.js → codemod-DWSN2erG.js} +523 -201
- package/dist/collect-B5BL3klN.js +1 -0
- package/dist/{collect-mLTY4nwb.js → collect-B7ZFAJU3.js} +36 -64
- package/dist/{collect-Ci6akVwH.js → collect-Bzv3le0A.js} +6 -6
- package/dist/{compare-pfyVnBjE.js → compare-DTfyUunF.js} +2 -2
- package/dist/compile/index.d.ts +2 -2
- package/dist/compile/index.js +1 -1
- package/dist/{compile-1bsAezL0.d.ts → compile-BFzysW3o.d.ts} +36 -12
- package/dist/{compile-CMqMx_IN.js → compile-EzEMB-7v.js} +77 -33
- package/dist/{completion-BTFrHZhX.js → completion-saoYiRFO.js} +17 -8
- package/dist/config-BwPBfN0t.js +108 -0
- package/dist/{config-Bj2Ixq3d.js → config-DBeXXmBF.js} +2 -2
- package/dist/{config-report-DSpsLpCE.js → config-report-BipSWA6V.js} +9 -6
- package/dist/config-shape-ykV0Vm2n.js +134 -0
- package/dist/{connection-DjhIJSs2.js → connection-B72dg5Q2.js} +23 -22
- package/dist/connector-schema-CYLQNMYq.js +434 -0
- package/dist/{context-B_u3_bDJ.js → context-BnIzxTNE.js} +4 -3
- package/dist/convex/index.d.ts +13 -7
- package/dist/convex/index.js +9 -6
- package/dist/core-H-mbhpZc.js +341 -0
- package/dist/core-hWIARPDz.d.ts +206 -0
- package/dist/{create-CBMmFie-.d.ts → create-BSNrVuxh.d.ts} +87 -34
- package/dist/{create-B30OMUOJ.d.ts → create-BVq1lNQE.d.ts} +49 -28
- package/dist/{create-D_NmPOdH.d.ts → create-CM1FciYG.d.ts} +37 -29
- package/dist/{create-CGm28gyI.js → create-Cq4Uox0f.js} +13 -9
- package/dist/{create-DCF2SvoT.js → create-_noSrY1T.js} +15 -21
- package/dist/{credential-BcoiejpY.js → credential-CyyyvCsL.js} +42 -39
- package/dist/{credentials-DPyHmYz5.js → credentials-CdDxp7jN.js} +6 -6
- package/dist/{deciding-columns-Sq12mEA6.js → deciding-columns-NB4wcyBf.js} +13 -2
- package/dist/{delegation-j6-rc37o.js → delegation-CDB6_keF.js} +31 -22
- package/dist/delivery/index.d.ts +5 -5
- package/dist/delivery/index.js +6 -5
- package/dist/{delivery-DCAgNyCn.js → delivery-BupfvJIV.js} +81 -20
- package/dist/{delivery-event-xX4obB8F.js → delivery-event-JioK5YbS.js} +6 -6
- package/dist/denied-text-CHNlVInN.d.ts +22 -0
- package/dist/{describe-BybMRY7A.js → describe-Bb4hGK5d.js} +6 -5
- package/dist/{describe-B5qgDvEW.d.ts → describe-ByS9LgHK.d.ts} +5 -5
- package/dist/{device-BARQ8eYG.js → device-BwbJNmb2.js} +11 -8
- package/dist/{diff-CmuSD3vc.js → diff-CN8IYhC_.js} +2 -2
- package/dist/{diff-3jbx6Rm4.js → diff-iZWV65nC.js} +23 -14
- package/dist/{digest-B3kc_PgQ.d.ts → digest-8Yx2CchM.d.ts} +3 -3
- package/dist/{doctor-BdZw53-k.js → doctor-CHD1M9R5.js} +485 -141
- package/dist/{doctor-3ZqNHX_G.js → doctor-D3wo-yuz.js} +4 -4
- package/dist/drizzle/index.d.ts +33 -19
- package/dist/drizzle/index.js +17 -9
- package/dist/elysia/index.d.ts +17 -11
- package/dist/elysia/index.js +5 -5
- package/dist/{errors-BKy0EfhN.js → errors-B6R_lg2a.js} +13 -4
- package/dist/{errors-BWXBMaKK.d.ts → errors-CHwoRRpB.d.ts} +4 -1
- package/dist/{errors-CWTY6uih.js → errors-Vpu6rKHl.js} +14 -10
- package/dist/{errors-DH5QEGXh.d.ts → errors-zi_4T-rx.d.ts} +22 -6
- package/dist/{evaluate-CNClBvcj.js → evaluate-Dd8Ta0Ie.js} +185 -80
- package/dist/{evaluations-beAooV0n.js → evaluations-IBceAeKT.js} +41 -20
- package/dist/{evaluations-C_1vdSZc.d.ts → evaluations-z8dQFDaN.d.ts} +12 -6
- package/dist/eve/index.d.ts +9 -6
- package/dist/eve/index.js +4 -4
- package/dist/{event-time-CsegxXxB.js → event-time-BGy48400.js} +8 -4
- package/dist/{expiry-D4_YKRKD.js → expiry-6XByK0dm.js} +8 -5
- package/dist/express/index.d.ts +9 -6
- package/dist/express/index.js +9 -6
- package/dist/fastify/index.d.ts +16 -10
- package/dist/fastify/index.js +8 -5
- package/dist/{fetch-adapter-Dw0MTS3D.d.ts → fetch-adapter-C1DCvpqV.d.ts} +9 -6
- package/dist/{fetch-adapter-DD_gfmrC.js → fetch-adapter-JL43jD3E.js} +14 -11
- package/dist/fields-D0vUI6dq.js +498 -0
- package/dist/{files-C5ZI27eV.js → files-B3AA05f1.js} +21 -5
- package/dist/{files-D_JIzUrP.js → files-BSjrtIHw.js} +11 -4
- package/dist/{fixtures-B7XrLeY7.js → fixtures-A1_sp_tR.js} +15 -6
- package/dist/{flags-C3DktBDY.js → flags-DXjoTVbS.js} +2 -2
- package/dist/{freeze-CrU8nQeX.js → freeze-jjan0lCv.js} +4 -1
- package/dist/{from-snapshot-CTCMsFZk.js → from-snapshot-DCakWRsr.js} +26 -16
- package/dist/gen-B5kvbAoZ.js +58 -0
- package/dist/gen-DWCYcMQM.js +827 -0
- package/dist/generate-CRVV5V7l.js +165 -0
- package/dist/generate-CezbCaTI.js +275 -0
- package/dist/{grantee-hxrUsbpM.js → grantee-Dke-9rd1.js} +4 -4
- package/dist/{grantee-conditions-CcXf8hMM.js → grantee-conditions-BrYPe-w0.js} +12 -12
- package/dist/{grantee-match-DbENBHLB.js → grantee-match-7RZZvoT_.js} +12 -6
- package/dist/{graph-sql-B-dsAT6j.d.ts → graph-sql-BwFC4Qjs.d.ts} +3 -3
- package/dist/h3/index.d.ts +10 -7
- package/dist/h3/index.js +3 -3
- package/dist/headers-B9jQxjtW.d.ts +10 -0
- package/dist/{helpers-CBYVMjwh.js → helpers-DJicaG6f.js} +40 -28
- package/dist/hono/index.d.ts +16 -10
- package/dist/hono/index.js +5 -5
- package/dist/{hosted-BPk51-kX.d.ts → hosted-Cmtxogd0.d.ts} +44 -20
- package/dist/{hosted-B46-5LYE.js → hosted-D0IzESu_.js} +68 -25
- package/dist/{http-ujKm0JXE.js → http-DH94eh6f.js} +4 -4
- package/dist/{http-BBoKefeS.js → http-DKLXrFGk.js} +13 -7
- package/dist/{http-DXJ37RDA.js → http-DOL33cO0.js} +11 -8
- package/dist/{import-wyqXUyjm.js → import-CST57XLp.js} +64 -51
- package/dist/{import-CNV7I2Dw.js → import-_YqhhwvX.js} +6 -6
- package/dist/{index-B8YjkEZ4.d.ts → index-B8s204GL.d.ts} +64 -52
- package/dist/{index-Ch8LpNDp.d.ts → index-BT5-1VsX.d.ts} +64 -25
- package/dist/{index-DuWxc5dt.d.ts → index-BW_0MZOS.d.ts} +388 -149
- package/dist/index-BhxEUY9J.d.ts +233 -0
- package/dist/index-Bv5iXPqU.d.ts +210 -0
- package/dist/{index-C-6Letxg.d.ts → index-_9UiQIkt.d.ts} +164 -77
- package/dist/index.d.ts +22 -21
- package/dist/index.js +35 -33
- package/dist/{init-GG6Jh8HD.js → init-Dz_PsHdv.js} +5 -5
- package/dist/{init-DeMYsnYJ.js → init-cE3A8aXq.js} +10 -10
- package/dist/{instance-options-BsVpyGIk.d.ts → instance-options-B0KXqTrJ.d.ts} +4 -4
- package/dist/{instance-snapshot-BOEhcqIF.js → instance-snapshot--JWiTrvO.js} +81 -56
- package/dist/{instant-BluqDvyS.js → instant-DK3Iz83e.js} +2 -2
- package/dist/{internals-Dk8A8fX3.js → internals-B3IgHrvo.js} +6 -3
- package/dist/{json-schema-B-vAwlde.js → json-schema-BPGa-RjQ.js} +9 -3
- package/dist/{json-schema-CN_iLFH2.d.ts → json-schema-BfXgnRNT.d.ts} +4 -4
- package/dist/jwt/index.d.ts +125 -17
- package/dist/jwt/index.js +157 -16
- package/dist/{kernel-D0NVb1cK.js → kernel-C-fYydV2.js} +151 -16
- package/dist/kysely/index.d.ts +49 -33
- package/dist/kysely/index.js +9 -7
- package/dist/{live-options-BaAFCIiu.js → live-options-BBIT68le.js} +2 -2
- package/dist/{load-DFS_ry0W.js → load-DjSU5KXO.js} +6 -6
- package/dist/load-Dm0pSYH3.js +1 -0
- package/dist/{login-Dfe5RlYk.js → login-D-8tMqzo.js} +4 -4
- package/dist/{login-imhuwvn3.js → login-DplQ0s84.js} +18 -15
- package/dist/{manifest-dvL2XEe1.js → manifest-Cgiq5oqc.js} +67 -37
- package/dist/{manifest-mx5ghSFM.d.ts → manifest-CzzWtnAb.d.ts} +144 -28
- package/dist/{manifest-schema-C0kK_8V0.js → manifest-schema-2r37XRhD.js} +80 -9
- package/dist/{manifest-C8IzW3Do.js → manifest-wg8WkkpD.js} +11 -7
- package/dist/{map-claims-D3oRmTk0.js → map-claims-BNWCJtmP.js} +27 -18
- package/dist/{map-error-Dr70oA_3.js → map-error-pasIGqMT.js} +7 -7
- package/dist/{markers-C6joDI3o.js → markers-BeHZ4x9Q.js} +20 -5
- package/dist/mcp/index.d.ts +154 -47
- package/dist/mcp/index.js +200 -41
- package/dist/{migrate-CIq4-E9F.js → migrate-B7oP_sQU.js} +102 -31
- package/dist/module-values-BJTBdo6y.js +107 -0
- package/dist/nest/index.d.ts +23 -13
- package/dist/nest/index.js +18 -11
- package/dist/next/client.d.ts +5 -2
- package/dist/next/client.js +1 -1
- package/dist/next/index.d.ts +72 -26
- package/dist/next/index.js +110 -6
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +4 -3
- package/dist/node/index.d.ts +5 -5
- package/dist/node/index.js +3 -3
- package/dist/{ocsf-BFp-PZZA.js → ocsf-TcjRBgIs.js} +15 -5
- package/dist/openai/index.d.ts +13 -7
- package/dist/openai/index.js +3 -3
- package/dist/openapi/index.d.ts +61 -30
- package/dist/openapi/index.js +2 -2
- package/dist/{openapi-B7HZ0f-r.js → openapi-BOsx0xn9.js} +34 -24
- package/dist/{openapi-C4dDTVEd.js → openapi-CBZUhA3l.js} +4 -5
- package/dist/{openapi-DD1SV-5-.js → openapi-VeHHU2l9.js} +23 -14
- package/dist/{operation-findings-DgATDpvh.js → operation-findings-CXtFzlKJ.js} +3 -3
- package/dist/{operations-X0VM7F3m.d.ts → operations-Ci_rT6Uo.d.ts} +10 -7
- package/dist/orpc/index.d.ts +68 -14
- package/dist/orpc/index.js +62 -13
- package/dist/otel/index.d.ts +7 -4
- package/dist/otel/index.js +8 -5
- package/dist/output-files-BHOIlrfD.js +1 -0
- package/dist/{output-files-DhRBBE4n.js → output-files-BIQ_9pIs.js} +29 -17
- package/dist/{output-1im__-08.js → output-tkju6U7R.js} +15 -7
- package/dist/ownership-B4L59T9u.js +1054 -0
- package/dist/parse-1_ZheTW5.js +151 -0
- package/dist/parse-local-manifest-BQhTlLQS.d.ts +10 -0
- package/dist/parse-local-manifest-DEiCF4ah.js +69 -0
- package/dist/{parse-request-D_tUXG6M.js → parse-request-CwCZpyjV.js} +7 -8
- package/dist/{paths-DXSHAMIa.js → paths-CUvURzHp.js} +4 -1
- package/dist/pdp/index.d.ts +9 -6
- package/dist/pdp/index.js +23 -12
- package/dist/{peer-CRvC7Hg1.js → peer-C9LvBCcb.js} +1 -1
- package/dist/peer-Q0fuZ6hx.js +982 -0
- package/dist/{permdock-SV8ZINYT.d.ts → permdock-B6Sf4T_S.d.ts} +270 -117
- package/dist/{permdock-BrAscV28.js → permdock-L-_I_3zP.js} +226 -74
- package/dist/{permission-registry-Br4jSNVi.js → permission-registry-B8tYxPIt.js} +24 -11
- package/dist/{vocabulary-CN8H_9UF.d.ts → permission-types-CAY-GU7e.d.ts} +130 -104
- package/dist/{permissions-u_KHg-sN.d.ts → permissions-Bx6UI8n1.d.ts} +6 -2
- package/dist/{permissions-B-wCP5ZC.js → permissions-CVXmRILq.js} +3 -3
- package/dist/{permissions-BPxlwVhN.js → permissions-mDcTWoBs.js} +8 -5
- package/dist/pg-DQTqRrou.js +60 -0
- package/dist/{pins-8ILTftC9.js → pins-nqarl3oE.js} +4 -1
- package/dist/pkce-CnmmYqUZ.js +21 -0
- package/dist/{policy-77FSAq_V.d.ts → policy-Ba4bDhaj.d.ts} +5 -5
- package/dist/{policy-DbM5YYtV.js → policy-CLQ8__GF.js} +49 -20
- package/dist/{policy-from-catalog-C2gMa2Dj.js → policy-from-catalog-CJgOm6aq.js} +28 -25
- package/dist/{policy-module-CTlwMvLI.js → policy-module-xym9fnSq.js} +12 -6
- package/dist/{policy-types-n0uRTsW8.d.ts → policy-types-CCiDRZBH.d.ts} +802 -334
- package/dist/postgres-store-DcyrPS-r.js +1028 -0
- package/dist/{postgrest-DAjUkF9w.js → postgrest-D4nuPXIQ.js} +88 -36
- package/dist/{postgrest-CEgRbefE.d.ts → postgrest-DicQyqm-.d.ts} +60 -30
- package/dist/powersync-DHNQX06W.js +1117 -0
- package/dist/{powersync-DISt6SUe.js → powersync-DXqGuq9A.js} +3 -3
- package/dist/powersync-Djf-zCpB.js +1 -0
- package/dist/preset/index.d.ts +38 -28
- package/dist/preset/index.js +1 -1
- package/dist/{preset-DZdU0tne.js → preset-BjLzeFpp.js} +58 -34
- package/dist/{principal-source-pHneukrx.js → principal-source-C6N60GtM.js} +10 -10
- package/dist/prisma/index.d.ts +69 -41
- package/dist/prisma/index.js +21 -17
- package/dist/{problem-CTviyH5L.js → problem-BVLHFK33.js} +41 -21
- package/dist/{problem-details-cn5X30gZ.js → problem-details-CLDgX1KV.js} +13 -4
- package/dist/{problem-types-CJ2325SZ.js → problem-types-DSsRfONQ.js} +4 -1
- package/dist/prompt-BYc6drYW.js +938 -0
- package/dist/protected-action-BE7N_Luo.d.ts +58 -0
- package/dist/protected-action-DUYqb-Ma.js +80 -0
- package/dist/{protected-BlF0F6wL.js → protected-action-QEA8arRB.js} +48 -14
- package/dist/{provider-B1al-AZz.js → provider-C55OGXJ7.js} +5 -4
- package/dist/{provider-DILL9oPK.d.ts → provider-DXfq5c2d.d.ts} +1 -1
- package/dist/{provider-store-DM6DNcta.js → provider-store-CaTv00tr.js} +13 -9
- package/dist/pull-B65ndFNZ.js +1 -0
- package/dist/{pull-jEwbf80J.js → pull-CwpGaQSi.js} +12 -11
- package/dist/{push-BD4f15co.js → push-DgssgzkK.js} +62 -16
- package/dist/{pushed-policy-DCW91KQU.d.ts → pushed-policy-D1Eoi6NM.d.ts} +12 -6
- package/dist/{pushed-policy-CqtxkV4G.js → pushed-policy-ITwYKvvH.js} +10 -7
- package/dist/react/index.d.ts +9 -8
- package/dist/react/index.js +7 -6
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -1
- package/dist/react-native/index.d.ts +104 -51
- package/dist/react-native/index.js +394 -67
- package/dist/{refs-K6eWUJ0H.js → refs-C8BKxc_e.js} +17 -3
- package/dist/{relation-walk-3OyDBF0S.js → relation-walk-DK_gkgL7.js} +12 -9
- package/dist/{relations-BnCWnJGI.js → relations-BxvRvDHK.js} +3 -3
- package/dist/{replay-Du1tJjG7.js → replay-BU09ad4L.js} +1 -1
- package/dist/{replay-Bkm-Ne4l.d.ts → replay-DNftIb_R.d.ts} +1 -1
- package/dist/{request-context-C81Pip9n.js → request-context-C25ilqiJ.js} +8 -8
- package/dist/{request-schema-CMreVuFM.js → request-schema-DhX7UbfQ.js} +4 -1
- package/dist/resend/index.d.ts +34 -28
- package/dist/resend/index.js +29 -29
- package/dist/resolve-names-CF7xXLSG.js +201 -0
- package/dist/{resource-relations-D_jw0BFx.js → resource-relations-8-pCScBO.js} +18 -9
- package/dist/{retire-CALjIQ7e.js → retire-B2xffQgY.js} +15 -6
- package/dist/{revocations-R07lFBnX.d.ts → revocations-BaUXHM1N.d.ts} +6 -3
- package/dist/{rls-DTvYd-lI.js → rls-CNEH2_BG.js} +743 -624
- package/dist/{rls-BAqP4q66.js → rls-Ca8Jz9b5.js} +41 -10
- package/dist/{role-builder-uWQZ4v84.js → role-builder-BK2-T6oD.js} +41 -20
- package/dist/rolldown-runtime-C3I60J0A.js +4 -0
- package/dist/{row-conditions-h2HythXQ.js → row-conditions-BAGHOmVq.js} +4 -3
- package/dist/{rpc-VSl5ylaQ.js → rpc-fNqAIqQk.js} +8 -5
- package/dist/{run-CUKuz0A8.js → run-Bz26OTfO.js} +26 -23
- package/dist/run-D-JE8QWb.js +1 -0
- package/dist/{run-ticket-DocwaTKn.d.ts → run-ticket-DbWI9f99.d.ts} +33 -13
- package/dist/{run-ticket-sxFI6LAk.js → run-ticket-Dv7aF7Dk.js} +29 -8
- package/dist/{runtime-D3VTAiCA.js → runtime-BGnrOkVH.js} +5 -2
- package/dist/{saas-BCXRuXca.js → saas-iWnUdwCt.js} +42 -29
- package/dist/{scan-qvOe0hS3.js → scan-DCj1KQfn.js} +1 -1
- package/dist/{schema-B4v0Qrac.js → schema-BOJjqlWL.js} +3 -2
- package/dist/{schema-yLuBWWsh.js → schema-Dst9I9Gs.js} +9 -5
- package/dist/scim/index.d.ts +3 -2
- package/dist/scim/index.js +3 -1
- package/dist/scim-D8YupzX1.js +44 -0
- package/dist/{scim-ClHtpyV3.js → scim-jJBJ7g1Z.js} +35 -551
- package/dist/{scopes-CUPSjo9q.js → scopes-0Up_UA2t.js} +42 -24
- package/dist/server/index.d.ts +12 -12
- package/dist/server/index.js +13 -13
- package/dist/{server-C3WXAHZj.js → server-3cVlTLde.js} +31 -31
- package/dist/{server-types-CKQiCnON.d.ts → server-types-CIlpSA2G.d.ts} +81 -46
- package/dist/{sha256-D9ihqmvd.js → sha256-C-4WXKIu.js} +4 -3
- package/dist/{shared-BAK8t4Uo.js → shared-Afctg7B0.js} +100 -61
- package/dist/{shared-DvvFzYXe.js → shared-l0oGGetg.js} +9 -6
- package/dist/{signals-D3NcTKUS.js → signals-B1vHR3fN.js} +2 -2
- package/dist/{sink-BEZSlMS_.js → sink-D9_vh372.js} +32 -21
- package/dist/{sink-4-eQIRWf.d.ts → sink-Dvi7YZG_.d.ts} +44 -20
- package/dist/{skills-B6lVdD_W.js → skills-Bjj7BNEQ.js} +16 -12
- package/dist/{skills-CTYwkl44.js → skills-OuoMR49L.js} +3 -3
- package/dist/{snapshot-B7DkYg6g.js → snapshot-CXlzi69u.js} +21 -18
- package/dist/{snapshot-cache-PXI-vGpY.d.ts → snapshot-cache-BAXZ0KOp.d.ts} +21 -12
- package/dist/{snapshot-cache-DZHQ0KF6.js → snapshot-cache-Bx_4xC_6.js} +14 -11
- package/dist/solid/index.d.ts +55 -15
- package/dist/solid/index.js +46 -12
- package/dist/solid-start/index.d.ts +30 -12
- package/dist/solid-start/index.js +3 -3
- package/dist/sources-B1Os_YUu.d.ts +312 -0
- package/dist/{sources-vs5dGOsS.js → sources-CMAM-2A3.js} +156 -51
- package/dist/sources-qoIJQrJv.js +11 -0
- package/dist/{sql-B5T1IL83.js → sql-0WjxIp9g.js} +32 -7
- package/dist/sql-B80DFdpG.d.ts +10 -0
- package/dist/{src-Cu2m75uo.js → src-DZ0l9UAg.js} +305 -123
- package/dist/ssf/index.d.ts +4 -4
- package/dist/ssf/index.js +665 -224
- package/dist/{standard-webhooks-rbD7LS6h.d.ts → standard-webhooks-C-qX4mJW.d.ts} +7 -7
- package/dist/{standard-webhooks-DgFFD-Ga.js → standard-webhooks-D67a3nzk.js} +5 -5
- package/dist/{store-BDutJkDL.js → store-RNFvaJJv.js} +43 -29
- package/dist/{store-options-u0ycEe8s.js → store-options-Dil3lraY.js} +105 -15
- package/dist/stores-C0Kta5Ft.d.ts +59 -0
- package/dist/{stores-CGhM8BN_.js → stores-DxZWDXCs.js} +42 -5
- package/dist/{stream-CvdUS9V1.js → stream-CDvF0sJm.js} +5 -5
- package/dist/{stream-CeCMJmBB.d.ts → stream-ltuLdtTG.d.ts} +4 -3
- package/dist/{style-DiKWnAvn.js → style-DblOtSga.js} +1 -1
- package/dist/{style-Uao76wO7.js → style-Ds11Hmk4.js} +3 -1
- package/dist/{subject-settings-CkEwrw3W.js → subject-settings-8sH5HlII.js} +8 -4
- package/dist/{row-check-Dt35czLy.d.ts → subject-settings-vNW8qmUH.d.ts} +30 -18
- package/dist/supabase/index.d.ts +48 -37
- package/dist/supabase/index.js +6 -6
- package/dist/supabase/middleware.d.ts +19 -13
- package/dist/supabase/middleware.js +7 -7
- package/dist/{supabase-x5JtR2tc.js → supabase-BL1qJzzY.js} +82 -36
- package/dist/{supabase-By_frk3U.js → supabase-D39cuGPt.js} +3 -3
- package/dist/{supabase-hook-BMoEhZir.js → supabase-hook-B-yXnzUl.js} +1206 -286
- package/dist/supabase-hook-H-MtE9Wd.js +1 -0
- package/dist/supautils-BU-pAEpf.js +808 -0
- package/dist/{support-session-C6xJQYut.js → support-session-BsmPCbz8.js} +22 -22
- package/dist/svelte/PermissionBoundary.svelte +2 -6
- package/dist/svelte/Protected.svelte +2 -8
- package/dist/svelte/index.d.ts +9 -8
- package/dist/svelte/index.js +10 -9
- package/dist/svelte/runtime.d.ts +4 -4
- package/dist/svelte/runtime.js +4 -4
- package/dist/svelte/source.d.ts +9 -8
- package/dist/svelte/source.js +6 -5
- package/dist/sveltekit/index.d.ts +30 -12
- package/dist/sveltekit/index.js +2 -2
- package/dist/{ownership-DqaJNJPn.js → tables-DeMNEy6V.js} +1025 -1085
- package/dist/tanstack-start/index.d.ts +22 -10
- package/dist/tanstack-start/index.js +2 -2
- package/dist/{target-Bw37TFNA.js → target-qdEAnnhU.js} +6 -6
- package/dist/{tenancy-DN2QAx9z.js → tenancy-5d1zVBvK.js} +36 -21
- package/dist/terminal/index.d.ts +23 -14
- package/dist/terminal/index.js +17 -20
- package/dist/testing/core.d.ts +2 -0
- package/dist/testing/core.js +1 -0
- package/dist/testing/fixtures/index.d.ts +15 -0
- package/dist/testing/fixtures/index.js +831 -0
- package/dist/testing/index.d.ts +235 -285
- package/dist/testing/index.js +502 -927
- package/dist/testing/saas/index.d.ts +43 -27
- 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-BtfSmdUo.js +444 -0
- package/dist/{thenable-yD7SabqG.js → thenable-BNSM1U2R.js} +12 -3
- package/dist/{time-DkWjdL98.js → time-nR1JesoH.js} +32 -8
- package/dist/{token-BLZcjQgt.js → token-DC9EQiet.js} +15 -13
- package/dist/{tool-refusal-CMLN5xhn.js → tool-refusal-Qpjs_W6M.js} +5 -2
- package/dist/trpc/index.d.ts +11 -8
- package/dist/trpc/index.js +15 -9
- package/dist/type-helpers-BIPIDa6a.d.ts +50 -0
- package/dist/{types-8RnwUAvE.d.ts → types-AMJ5qoU_.d.ts} +49 -30
- package/dist/{types-C_IiEs64.d.ts → types-B2mlGuhW.d.ts} +14 -11
- package/dist/{types-_WhUP7ud.d.ts → types-B4uqKDnp.d.ts} +25 -12
- package/dist/{types-CX7AvRqP.d.ts → types-BJk0BKRX.d.ts} +24 -14
- package/dist/types-B_d_g7T3.d.ts +262 -0
- package/dist/{types-BdtSDbBO.d.ts → types-B_tiYvk8.d.ts} +59 -32
- package/dist/{types-DqzsnlSA.js → types-CcUXciSj.js} +17 -5
- package/dist/{types-Dfv0aTER.d.ts → types-DC1Loeh3.d.ts} +449 -168
- package/dist/{types-lUlG3TVd.d.ts → types-DHKPcqzr.d.ts} +2 -2
- package/dist/{types-C791HFu9.d.ts → types-DjuMBNF9.d.ts} +68 -32
- package/dist/{types-C863OQiR.d.ts → types-HzqOtvsJ.d.ts} +23 -12
- package/dist/{types-Druu9wLM.d.ts → types-ZzRGoViD.d.ts} +77 -24
- package/dist/{types-BzzaHxt3.d.ts → types-ah9NlU6j.d.ts} +137 -48
- package/dist/{types-CVZSNocA.d.ts → types-djVzLlIV.d.ts} +31 -14
- package/dist/{types-BtHtcKMr.d.ts → types-yUuPR5Hy.d.ts} +47 -24
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +2 -2
- package/dist/usage-CYeXek0p.js +1 -0
- package/dist/{usage-SveTRTyr.js → usage-CxEzS4ey.js} +50 -26
- package/dist/{usage-CRVl9V3t.js → usage-ab0PdusX.js} +2 -2
- package/dist/{validation-error-ZfUHbKvf.js → validation-error-CYg2kJEw.js} +4 -1
- package/dist/{validation-error-DHUrAkku.d.ts → validation-error-YGg2mXyH.d.ts} +5 -2
- package/dist/{validity-CZ7-y0Cj.js → validity-Buc7w8E9.js} +13 -10
- package/dist/{verifier-DHuh-okk.js → verifier-DUV6pLqZ.js} +10 -4
- package/dist/{verify-BaAKx7t6.js → verify-DZeqoWu5.js} +417 -489
- package/dist/version-DMQ0dnN5.js +48 -0
- package/dist/version-DizhmD72.js +1 -0
- package/dist/{views-DTIIO-oQ.js → views-CD8wuL4Z.js} +19 -6
- package/dist/vocabulary-DEJ5j4mR.d.ts +70 -0
- package/dist/vue/index.d.ts +71 -21
- package/dist/vue/index.js +50 -13
- package/dist/{waiter-B986w1oK.d.ts → waiter-Bu9zAlE0.d.ts} +13 -10
- package/dist/{waiter-BGUtGYoU.js → waiter-aBLM63_f.js} +1 -1
- package/dist/{watch-vea54PRw.js → watch-Cx_WciLE.js} +8 -8
- package/dist/watch-DB42U1ib.js +1 -0
- package/dist/webmcp/index.d.ts +11 -11
- package/dist/webmcp/index.js +8 -5
- package/dist/{where-scope-C9vfEphl.js → where-scope-DlCdCM_8.js} +56 -30
- package/dist/{who-can-DWBAcaCQ.js → who-can-rI7dCR9Q.js} +23 -20
- package/dist/workflow/index.d.ts +3 -3
- package/dist/workflow/index.js +13 -13
- package/dist/workflow-sdk/index.d.ts +19 -13
- package/dist/workflow-sdk/index.js +6 -6
- package/dist/{wrap-gX61EtpB.js → wrap-B2wziJI_.js} +11 -5
- package/dist/{wrap-C5jC6sKI.d.ts → wrap-DlJE3gS_.d.ts} +8 -5
- package/package.json +25 -14
- package/schemas/catalog-v1.json +3 -0
- 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 +308 -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-credentials/references/tokens-keys-links.md +8 -6
- package/skills/permdock-data/references/queries-and-rls.md +6 -2
- package/skills/permdock-tenancy/SKILL.md +1 -1
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +2 -1
- package/skills/permdock-wire/references/adapters.md +28 -22
- 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/config-bs_kZeYq.js +0 -162
- package/dist/denied-text-CiFQNuMV.d.ts +0 -19
- package/dist/fields-OYG_JR78.js +0 -203
- package/dist/generate-CNU1pIOo.js +0 -171
- package/dist/headers-BIjfMaoC.js +0 -8
- package/dist/headers-CIbDTUTK.d.ts +0 -7
- package/dist/index-Cp_ilKi1.d.ts +0 -91
- package/dist/index-CueFnxQc.d.ts +0 -110
- package/dist/load-BxEtwlBS.js +0 -1
- package/dist/output-files-B3IZJksC.js +0 -1
- package/dist/package-root-Dk_ETdkp.js +0 -31
- package/dist/parse-Cxq0WxrA.js +0 -53
- package/dist/parse-local-manifest-5ODwqu8b.d.ts +0 -10
- package/dist/parse-local-manifest-CVSs6J9I.js +0 -34
- package/dist/peer-C8swJ2GH.js +0 -50
- package/dist/policies-DP2nqRoF.js +0 -295
- package/dist/powersync-BkWWCS1K.js +0 -538
- package/dist/powersync-MMJ2mI2q.js +0 -1
- package/dist/problem-details-B45V5d2b.d.ts +0 -7
- package/dist/prompt-DfZLJLIn.js +0 -32
- package/dist/protected-RFGpakN9.d.ts +0 -38
- package/dist/pull-CvpINcMK.js +0 -1
- package/dist/run-DDwUaJDU.js +0 -1
- package/dist/sources-Xc4zkszI.d.ts +0 -190
- package/dist/stores-BUGNbJTt.d.ts +0 -30
- 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/types-B3_ooZgZ.d.ts +0 -172
- package/dist/usage-CZpSQP6L.js +0 -1
- package/dist/version-CEbQ5UeM.js +0 -18
- package/dist/version-DXkYUISx.js +0 -1
- package/dist/watch--ovlPOc0.js +0 -1
|
@@ -1,41 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as listPermissions, h as formerKeys } from "./permission-registry-
|
|
3
|
-
import { u as isPrincipalRelation } from "./resource-relations-
|
|
4
|
-
import { t as canonicalJson } from "./canonical-json-
|
|
5
|
-
import {
|
|
6
|
-
import { o as flattenGrantee } from "./grantee-
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { s as parseCredential } from "./credential-
|
|
10
|
-
import { a as describeError } from "./errors-
|
|
11
|
-
import { t as policyRowConditionKeys } from "./row-conditions-
|
|
12
|
-
import { a as rel, n as doctorSrcPath, o as sqlFiles, r as listSourceFiles } from "./files-
|
|
13
|
-
import { i as DOCTOR_REPORT_SCHEMA } from "./version-
|
|
14
|
-
import {
|
|
15
|
-
import { a as loadModule, i as loadConfiguredPolicy, n as asPolicy, s as pickNamed } from "./load-
|
|
16
|
-
import { r as runCollect } from "./collect-mLTY4nwb.js";
|
|
1
|
+
import { o as hasConditionOp } from "./refs-C8BKxc_e.js";
|
|
2
|
+
import { c as listPermissions, h as formerKeys } from "./permission-registry-B8tYxPIt.js";
|
|
3
|
+
import { u as isPrincipalRelation } from "./resource-relations-8-pCScBO.js";
|
|
4
|
+
import { t as canonicalJson } from "./canonical-json-DwilZ1iB.js";
|
|
5
|
+
import { f as scopeList, o as normalizeMemberships, s as resolveScope } from "./scopes-0Up_UA2t.js";
|
|
6
|
+
import { o as flattenGrantee } from "./grantee-Dke-9rd1.js";
|
|
7
|
+
import { u as separationConflicts } from "./policy-CLQ8__GF.js";
|
|
8
|
+
import { T as validateCustomRole } from "./evaluate-Dd8Ta0Ie.js";
|
|
9
|
+
import { s as parseCredential } from "./credential-CyyyvCsL.js";
|
|
10
|
+
import { a as describeError } from "./errors-Vpu6rKHl.js";
|
|
11
|
+
import { t as policyRowConditionKeys } from "./row-conditions-BAGHOmVq.js";
|
|
12
|
+
import { a as rel, n as doctorSrcPath, o as sqlFiles, r as listSourceFiles } from "./files-BSjrtIHw.js";
|
|
13
|
+
import { i as DOCTOR_REPORT_SCHEMA } from "./version-DMQ0dnN5.js";
|
|
14
|
+
import { c as jsonSchemaOf, i as catalogOAuthScopes } from "./catalog-doc-DjMKzJp9.js";
|
|
15
|
+
import { a as loadModule, i as loadConfiguredPolicy, n as asPolicy, s as pickNamed } from "./load-DjSU5KXO.js";
|
|
17
16
|
import { t as catalogPath } from "./catalog-path-CnI0XTFK.js";
|
|
18
|
-
import {
|
|
19
|
-
import { s as quoteSqlLiteral } from "./sql-
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import "./
|
|
23
|
-
import { t as
|
|
24
|
-
import
|
|
25
|
-
import { t as
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
17
|
+
import { r as runCollect } from "./collect-B7ZFAJU3.js";
|
|
18
|
+
import { i as qualifySqlTable, s as quoteSqlLiteral } from "./sql-0WjxIp9g.js";
|
|
19
|
+
import { a as scopeMembershipTable } from "./compile-EzEMB-7v.js";
|
|
20
|
+
import { n as supabaseTenantClaim } from "./budget-BELja5A9.js";
|
|
21
|
+
import { d as keptKeys, r as PERMDOCK_SCHEMA } from "./sources-CMAM-2A3.js";
|
|
22
|
+
import { t as parseCatalog } from "./parse-1_ZheTW5.js";
|
|
23
|
+
import "./markers-BeHZ4x9Q.js";
|
|
24
|
+
import { t as didYouMean } from "./args-0FrVs5EH.js";
|
|
25
|
+
import { t as DOCS_BASE } from "./manifest-Cgiq5oqc.js";
|
|
26
|
+
import { t as createStyle } from "./style-Ds11Hmk4.js";
|
|
27
|
+
import { a as nonEmptyOr, r as findingOf } from "./output-tkju6U7R.js";
|
|
28
|
+
import { i as runSkillsInstall } from "./skills-Bjj7BNEQ.js";
|
|
28
29
|
import { n as MIGRATION_DIRS, t as DEFAULT_SENSITIVE_ACTIONS } from "./constants-DcJABbLW.js";
|
|
29
|
-
import { l as partPath } from "./output-files-
|
|
30
|
-
import { _ as
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import { t as
|
|
36
|
-
import {
|
|
37
|
-
import { n as
|
|
38
|
-
import {
|
|
30
|
+
import { l as partPath } from "./output-files-BIQ_9pIs.js";
|
|
31
|
+
import { _ as supabaseConfig, a as supabaseHookManifest, c as pd039, g as readSupabaseConfig, h as pd074, m as pd045, n as hookOut, p as attrsPlan, u as resolveSchemas } from "./supabase-hook-B-yXnzUl.js";
|
|
32
|
+
import { G as byKey, Q as graphSqlName, n as tableNameOf, o as graphPlan, r as tableNames } from "./tables-DeMNEy6V.js";
|
|
33
|
+
import { a as guardedTables, p as contextRefs, r as unsatisfiedKinds, t as ownershipRules } from "./ownership-B4L59T9u.js";
|
|
34
|
+
import { t as membershipSourcesOf } from "./sources-qoIJQrJv.js";
|
|
35
|
+
import { i as group, n as membershipColumns, o as sqlStatements, r as tableKey } from "./deciding-columns-NB4wcyBf.js";
|
|
36
|
+
import { t as resolveNames } from "./resolve-names-CF7xXLSG.js";
|
|
37
|
+
import { t as FIELD_VIEWS } from "./fields-D0vUI6dq.js";
|
|
38
|
+
import { a as pd010, c as pd014, d as pd038, f as pd041, i as pd008, l as pd015, n as pd001, o as pd011, p as pd057, r as pd007, s as pd013, t as runUsage, u as pd036 } from "./usage-CxEzS4ey.js";
|
|
39
|
+
import { d as rowConditionMessage, f as commandFor, m as tableFor, p as compileGrants, s as ROW_CONDITION_FIX, u as helperTablePolicies } from "./verify-DZeqoWu5.js";
|
|
40
|
+
import { a as streamOf, i as scopeSourcesOf, r as staleFiles, t as powersyncFiles } from "./powersync-DHNQX06W.js";
|
|
41
|
+
import { a as generateOptions, c as buildContext, n as inheritTargets, o as isRefusal, t as supautilsRejections } from "./supautils-BU-pAEpf.js";
|
|
42
|
+
import { n as legacyHelperNames, r as retirePlan, t as legacyHelperCalls } from "./retire-B2xffQgY.js";
|
|
39
43
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
40
44
|
import { basename, dirname, join, resolve } from "node:path";
|
|
41
45
|
import { createRequire } from "node:module";
|
|
@@ -169,7 +173,10 @@ function membershipsFixture(input) {
|
|
|
169
173
|
//#endregion
|
|
170
174
|
//#region src/cli/doctor/assignments.ts
|
|
171
175
|
const TRIGGER = new RegExp(String.raw`^create\s+(?:or\s+replace\s+)?trigger\s+"?permdock_assignment"?\s[\s\S]*?\bon\s+((?:"[^"]+"|\w+)(?:\.(?:"[^"]+"|\w+))?)`, "iu");
|
|
172
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* The tables a `permdock_assignment` trigger is created on in the migrations
|
|
178
|
+
* and the `rls generate` output.
|
|
179
|
+
*/
|
|
173
180
|
function triggeredTables(input) {
|
|
174
181
|
const out = input.config.rls?.out ?? "rls.sql";
|
|
175
182
|
const files = sqlFiles(input.cwd, [...input.config.doctor?.migrations ?? MIGRATION_DIRS, ...new Set([
|
|
@@ -185,10 +192,10 @@ function triggeredTables(input) {
|
|
|
185
192
|
return tables;
|
|
186
193
|
}
|
|
187
194
|
/**
|
|
188
|
-
* PD064: `rls.assignments` guards a table (a membership table, the
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
195
|
+
* PD064: `rls.assignments` guards a table (a membership table, the global-roles
|
|
196
|
+
* table or a listed table) that no `permdock_assignment` trigger is created on
|
|
197
|
+
* in the migrations or the `rls generate` output, so a client may write any
|
|
198
|
+
* role there.
|
|
192
199
|
*/
|
|
193
200
|
async function pd064(input) {
|
|
194
201
|
if (input.config.rls?.assignments === void 0) return [];
|
|
@@ -207,8 +214,8 @@ async function pd064(input) {
|
|
|
207
214
|
//#endregion
|
|
208
215
|
//#region src/cli/doctor/catalog.ts
|
|
209
216
|
/**
|
|
210
|
-
* A policy that declares `oauthScopes` against a catalog on disk without
|
|
211
|
-
*
|
|
217
|
+
* A policy that declares `oauthScopes` against a catalog on disk without them,
|
|
218
|
+
* or with another set. `authorizationProvider` builds better-supabase's
|
|
212
219
|
* `delegation.scopes` from the catalog, so its SQL checks deny a token that
|
|
213
220
|
* holds only the coarse scope.
|
|
214
221
|
*/
|
|
@@ -289,6 +296,72 @@ async function pd067(input) {
|
|
|
289
296
|
}]));
|
|
290
297
|
}
|
|
291
298
|
//#endregion
|
|
299
|
+
//#region src/cli/doctor/config-names.ts
|
|
300
|
+
const PLACEHOLDER = /\{([^{}]*)\}/gu;
|
|
301
|
+
/** The scope aliases `ScopeNames` admits next to the declared names. */
|
|
302
|
+
const ALIASES = ["tenant", "team"];
|
|
303
|
+
function keysOf(record) {
|
|
304
|
+
return record === void 0 ? [] : Object.keys(record);
|
|
305
|
+
}
|
|
306
|
+
function named(where, kind, names) {
|
|
307
|
+
return names.map((name) => ({
|
|
308
|
+
where,
|
|
309
|
+
kind,
|
|
310
|
+
name
|
|
311
|
+
}));
|
|
312
|
+
}
|
|
313
|
+
function namedIn(config) {
|
|
314
|
+
const rls = config.rls;
|
|
315
|
+
return [
|
|
316
|
+
...named("rls.tables", "resource", keysOf(rls?.tables)),
|
|
317
|
+
...named("rls.scopeTypes", "scope", keysOf(rls?.scopeTypes)),
|
|
318
|
+
...named("rls.actions", "action", keysOf(rls?.actions)),
|
|
319
|
+
...named("rls.memberships.scopes", "scope", keysOf(rls?.memberships?.scopes)),
|
|
320
|
+
...named("rls.suspension.scopes", "scope", keysOf(rls?.suspension?.scopes)),
|
|
321
|
+
...Object.keys(rls?.realtime?.topics ?? {}).flatMap((pattern) => named(`rls.realtime.topics['${pattern}']`, "scope", [...pattern.matchAll(PLACEHOLDER)].map((match) => match[1] ?? ""))),
|
|
322
|
+
...Object.entries(rls?.storage?.buckets ?? {}).flatMap(([name, bucket]) => named(`rls.storage.buckets.${name}.scope`, "scope", [bucket.scope])),
|
|
323
|
+
...named("powersync.resources", "resource", config.powersync?.resources ?? []),
|
|
324
|
+
...named("powersync.action", "action", config.powersync?.action === void 0 ? [] : [config.powersync.action])
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* PD077: a config key or value names a scope, resource or action the policy
|
|
329
|
+
* does not declare, such as a Realtime topic `{organisation}` placeholder for
|
|
330
|
+
* the scope `organization`. `defineConfig` with the policy value reports the
|
|
331
|
+
* same names at compile time. Also a SQL helper name `resolveNames` rejects:
|
|
332
|
+
* two sources sharing one, or one Postgres cannot hold.
|
|
333
|
+
*/
|
|
334
|
+
async function pd077(input) {
|
|
335
|
+
if (input.config.policy === void 0) return [];
|
|
336
|
+
const policy = await policyOf(input);
|
|
337
|
+
if (policy === void 0) return [];
|
|
338
|
+
const leaves = listPermissions(policy.vocabulary.permissions);
|
|
339
|
+
const declared = {
|
|
340
|
+
scope: /* @__PURE__ */ new Set([...policy.scopes.map((scope) => scope.name), ...ALIASES]),
|
|
341
|
+
resource: new Set(leaves.map((leaf) => leaf.resource)),
|
|
342
|
+
action: new Set(leaves.map((leaf) => leaf.action))
|
|
343
|
+
};
|
|
344
|
+
return [...namedIn(input.config).filter((entry) => !declared[entry.kind].has(entry.name)).map((entry) => ({
|
|
345
|
+
code: "PD077",
|
|
346
|
+
severity: "error",
|
|
347
|
+
message: `${entry.where} names the ${entry.kind} '${entry.name}', which the policy does not declare`,
|
|
348
|
+
fix: `use one of ${[...declared[entry.kind]].toSorted().join(", ")}, or pass the policy value to defineConfig to catch this at compile time`
|
|
349
|
+
})), ...helperNames(policy, input.config)];
|
|
350
|
+
}
|
|
351
|
+
function helperNames(policy, config) {
|
|
352
|
+
try {
|
|
353
|
+
resolveNames(policy, config);
|
|
354
|
+
return [];
|
|
355
|
+
} catch (cause) {
|
|
356
|
+
return [{
|
|
357
|
+
code: "PD077",
|
|
358
|
+
severity: "error",
|
|
359
|
+
message: describeError(cause).replace(/^PermDock CLI: /u, ""),
|
|
360
|
+
fix: "rename the scope or resource so each generated SQL helper has its own name of at most 63 bytes"
|
|
361
|
+
}];
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
//#endregion
|
|
292
365
|
//#region src/cli/doctor/declarative.ts
|
|
293
366
|
const SCHEMAS = "supabase/schemas";
|
|
294
367
|
const MIGRATIONS = "supabase/migrations";
|
|
@@ -300,7 +373,10 @@ const CALLS_HELPER = /\b(?:permdock_has|(?:permitted|member)_[a-z][a-z0-9_]*_ids
|
|
|
300
373
|
function uncommented(text) {
|
|
301
374
|
return text.replaceAll(/--[^\n]*/gu, "").replaceAll(/\/\*[\s\S]*?\*\//gu, "");
|
|
302
375
|
}
|
|
303
|
-
/**
|
|
376
|
+
/**
|
|
377
|
+
* Whether `text` starts with a line `rls generate` or `supabase hook generate`
|
|
378
|
+
* writes.
|
|
379
|
+
*/
|
|
304
380
|
function generatedByPermDock(text) {
|
|
305
381
|
return text.startsWith("-- permdock:") || text.startsWith("-- generated by permdock");
|
|
306
382
|
}
|
|
@@ -308,9 +384,9 @@ function generatedByPermDock(text) {
|
|
|
308
384
|
* PD042: under migra, a hook declared under `supabase/schemas` reaches the
|
|
309
385
|
* database through `supabase db diff`, which drops its `supabase_auth_admin`
|
|
310
386
|
* grants. pg-delta keeps them, so the check runs only for `migra`. A function
|
|
311
|
-
* created (or dropped and created again) without them is executable by
|
|
312
|
-
*
|
|
313
|
-
*
|
|
387
|
+
* created (or dropped and created again) without them is executable by `public`
|
|
388
|
+
* and not by the auth server, so a migration at or after the one that creates
|
|
389
|
+
* it must carry the grants.
|
|
314
390
|
*/
|
|
315
391
|
function pd042(cwd, config, files = nodeFiles) {
|
|
316
392
|
if (config.supabase?.hook === void 0 || resolveSchemas(cwd, config).mode !== "migra") return [];
|
|
@@ -338,10 +414,10 @@ function generatedSchemaFiles(cwd, schemaDir, files) {
|
|
|
338
414
|
}
|
|
339
415
|
/**
|
|
340
416
|
* PD068: PermDock generates for pg-delta (the default), but
|
|
341
|
-
* `supabase/config.toml` does not set `[experimental.pgdelta] enabled =
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
417
|
+
* `supabase/config.toml` does not set `[experimental.pgdelta] enabled = true`,
|
|
418
|
+
* so Supabase CLI 2.x diffs the schema files with migra. migra drops the hook's
|
|
419
|
+
* `supabase_auth_admin` grants and the helpers' privileges, and PermDock wrote
|
|
420
|
+
* no `--grants-out` file to restore them.
|
|
345
421
|
*/
|
|
346
422
|
function pd068(cwd, config, files = nodeFiles) {
|
|
347
423
|
const read = readSupabaseConfig(cwd);
|
|
@@ -360,8 +436,8 @@ function pd068(cwd, config, files = nodeFiles) {
|
|
|
360
436
|
/**
|
|
361
437
|
* PD069: leftovers of declarative schemas on migra in a pg-delta project:
|
|
362
438
|
* `[db.migrations] schema_paths`, which pg-delta's dependency order makes
|
|
363
|
-
* unnecessary, and numbered `NNN_permdock_<part>.sql` files that duplicate
|
|
364
|
-
*
|
|
439
|
+
* unnecessary, and numbered `NNN_permdock_<part>.sql` files that duplicate the
|
|
440
|
+
* per-schema layout `rls generate --split` now writes.
|
|
365
441
|
*/
|
|
366
442
|
function pd069(cwd, config, files = nodeFiles) {
|
|
367
443
|
const schemas = resolveSchemas(cwd, config);
|
|
@@ -381,20 +457,26 @@ function pd069(cwd, config, files = nodeFiles) {
|
|
|
381
457
|
});
|
|
382
458
|
return findings;
|
|
383
459
|
}
|
|
384
|
-
/**
|
|
460
|
+
/**
|
|
461
|
+
* The `[db.migrations] schema_paths` globs of `supabase/config.toml`, or
|
|
462
|
+
* Supabase's default.
|
|
463
|
+
*/
|
|
385
464
|
function schemaPaths(cwd) {
|
|
386
465
|
const paths = supabaseConfig(cwd).schemaPaths ?? [];
|
|
387
466
|
return paths.length === 0 ? ["./schemas/**/*.sql"] : paths;
|
|
388
467
|
}
|
|
389
|
-
/**
|
|
468
|
+
/**
|
|
469
|
+
* Schema files in the order `supabase db diff` applies them: each glob in turn,
|
|
470
|
+
* sorted within it.
|
|
471
|
+
*/
|
|
390
472
|
function orderedSchemaFiles(cwd) {
|
|
391
473
|
return sqlFiles(join(cwd, "supabase"), schemaPaths(cwd), { order: "entry" });
|
|
392
474
|
}
|
|
393
475
|
/**
|
|
394
|
-
* PD043: under migra, `db diff` applies schema files in `schema_paths`
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
476
|
+
* PD043: under migra, `db diff` applies schema files in `schema_paths` order,
|
|
477
|
+
* so a policy that calls `permdock_has` or `permitted_<scope>_ids` before the
|
|
478
|
+
* file that defines them fails to apply. pg-delta orders by dependencies
|
|
479
|
+
* instead.
|
|
398
480
|
*/
|
|
399
481
|
function pd043(cwd, config, files = nodeFiles) {
|
|
400
482
|
if (resolveSchemas(cwd, config).mode !== "migra") return [];
|
|
@@ -475,7 +557,7 @@ async function pd018(input) {
|
|
|
475
557
|
for (const conflict of separationConflicts(policy, record.memberships ?? [])) findings.push({
|
|
476
558
|
code: "PD018",
|
|
477
559
|
severity: "warning",
|
|
478
|
-
message: `membership ${conflict.principal
|
|
560
|
+
message: `membership ${nonEmptyOr(conflict.principal, "unknown")} holds exclusive roles ${conflict.roles.join(" and ")}`,
|
|
479
561
|
fix: "remove one of the exclusive roles from the membership fixture"
|
|
480
562
|
});
|
|
481
563
|
return findings;
|
|
@@ -510,9 +592,9 @@ async function pd023(input) {
|
|
|
510
592
|
return findings;
|
|
511
593
|
}
|
|
512
594
|
/**
|
|
513
|
-
* PD055: a stored custom role names a permission by a key it was renamed
|
|
514
|
-
*
|
|
515
|
-
*
|
|
595
|
+
* PD055: a stored custom role names a permission by a key it was renamed from.
|
|
596
|
+
* It still resolves; the alias can only be dropped once storage holds the
|
|
597
|
+
* current key.
|
|
516
598
|
*/
|
|
517
599
|
async function pd055(input) {
|
|
518
600
|
const files = filesOf(input);
|
|
@@ -538,7 +620,10 @@ async function pd055(input) {
|
|
|
538
620
|
});
|
|
539
621
|
return findings;
|
|
540
622
|
}
|
|
541
|
-
/**
|
|
623
|
+
/**
|
|
624
|
+
* Fixture API keys that never expire, credential policies that allow it, and
|
|
625
|
+
* records that are not v1 credentials.
|
|
626
|
+
*/
|
|
542
627
|
function pd029(input) {
|
|
543
628
|
const parsed = readFixture(input, input.config.doctor?.credentials);
|
|
544
629
|
const findings = [];
|
|
@@ -604,7 +689,10 @@ const ROUTE_GLOBS = ["**/api/permdock/route.{ts,tsx,js,jsx,mjs}"];
|
|
|
604
689
|
const ENDPOINT = /\bendpoint\s*(?::|=)\s*\{?\s*['"`]/u;
|
|
605
690
|
const NATIVE_IMPORT = /\bfrom\s*['"]permdock\/react-native['"]/u;
|
|
606
691
|
const CLIENT_READS = /* @__PURE__ */ new Set(["usePermission", "reference"]);
|
|
607
|
-
/**
|
|
692
|
+
/**
|
|
693
|
+
* A grant the snapshot marks `portable: false`, so a client check needs the
|
|
694
|
+
* endpoint.
|
|
695
|
+
*/
|
|
608
696
|
function serverOnly(policy, grant) {
|
|
609
697
|
if (!grant.portable || hasConditionOp(grant.where, "related")) return true;
|
|
610
698
|
const relations = policy.resources.get(grant.permission.resource)?.relations;
|
|
@@ -619,7 +707,10 @@ function serverOnlyKeys(policy) {
|
|
|
619
707
|
for (const grant of [...policy.grants, ...policy.roles.flatMap((binding) => binding.grants)]) if (grant.hosted === void 0 && grant.effect === "allow" && serverOnly(policy, grant)) keys.add(grant.permission.key);
|
|
620
708
|
return keys;
|
|
621
709
|
}
|
|
622
|
-
/**
|
|
710
|
+
/**
|
|
711
|
+
* An `app/**\/api/permdock/route.ts` that exports `permdockHandler`'s result,
|
|
712
|
+
* or an `endpoint` string in the source.
|
|
713
|
+
*/
|
|
623
714
|
function hasEndpoint(files, cwd, sources) {
|
|
624
715
|
if (listSourceFiles(cwd, ROUTE_GLOBS).filter((file) => /(?:^|[\\/])app[\\/]/u.test(rel(cwd, file))).some((file) => files.read(file).includes("permdockHandler"))) return true;
|
|
625
716
|
return sources.some((file) => ENDPOINT.test(files.read(file)));
|
|
@@ -751,11 +842,14 @@ async function pd019(input) {
|
|
|
751
842
|
fix: "set rls.authorize: 'database', or lower [auth] jwt_expiry in supabase/config.toml to 3600 or less"
|
|
752
843
|
}];
|
|
753
844
|
}
|
|
754
|
-
/** Fixture memberships the policy's scopes would drop: an undeclared scope, a missing parent id, mixed shapes. */
|
|
755
845
|
/**
|
|
756
|
-
*
|
|
757
|
-
*
|
|
758
|
-
|
|
846
|
+
* Fixture memberships the policy's scopes would drop: an undeclared scope, a
|
|
847
|
+
* missing parent id, mixed shapes.
|
|
848
|
+
*/
|
|
849
|
+
/**
|
|
850
|
+
* A scope whose roles can all be removed can end up with nobody able to manage
|
|
851
|
+
* it. Setting `min` on any of its roles (even `min: 0`) records the decision
|
|
852
|
+
* and silences the warning.
|
|
759
853
|
*/
|
|
760
854
|
async function pd026(input) {
|
|
761
855
|
if (input.config.policy === void 0) return [];
|
|
@@ -832,10 +926,28 @@ async function pd035(input) {
|
|
|
832
926
|
//#endregion
|
|
833
927
|
//#region src/cli/doctor/powersync.ts
|
|
834
928
|
/**
|
|
929
|
+
* Why each membership source of a scope the streams read through sources has no
|
|
930
|
+
* stream form, once per source.
|
|
931
|
+
*/
|
|
932
|
+
function sourceGaps(policy, config) {
|
|
933
|
+
const scopes = scopeList(policy.scopes);
|
|
934
|
+
const sources = membershipSourcesOf(config);
|
|
935
|
+
const gaps = /* @__PURE__ */ new Set();
|
|
936
|
+
for (const scope of scopes) {
|
|
937
|
+
if (scopeMembershipTable(config.rls?.memberships, scopes, scope.name) !== void 0) continue;
|
|
938
|
+
for (const source of scopeSourcesOf(sources, scope.name)) {
|
|
939
|
+
const stream = streamOf(source);
|
|
940
|
+
if (typeof stream === "string") gaps.add(stream);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return [...gaps];
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
835
946
|
* PD058: `sync-config.yaml` or the `powersync.manifest` file is not what the
|
|
836
|
-
* policy compiles to, so PowerSync syncs rows, or the device builds
|
|
837
|
-
*
|
|
838
|
-
*
|
|
947
|
+
* policy compiles to, so PowerSync syncs rows, or the device builds snapshots,
|
|
948
|
+
* by an older policy. A policy that does not compile is a finding too, and so
|
|
949
|
+
* is each membership source of a synced scope with no stream form: its members
|
|
950
|
+
* sync none of the rows it grants.
|
|
839
951
|
*/
|
|
840
952
|
async function pd058(input) {
|
|
841
953
|
const { cwd, config } = input;
|
|
@@ -853,12 +965,18 @@ async function pd058(input) {
|
|
|
853
965
|
fix: "fix the powersync and rls config, then run permdock powersync generate"
|
|
854
966
|
}];
|
|
855
967
|
}
|
|
856
|
-
|
|
968
|
+
const gaps = sourceGaps(policy, config).map((gap) => ({
|
|
969
|
+
code: "PD058",
|
|
970
|
+
severity: "warning",
|
|
971
|
+
message: gap,
|
|
972
|
+
fix: "build the source with tableMembershipSource or junctionMembershipSource, without expiresAt or disabledAt, or serve its members from the server"
|
|
973
|
+
}));
|
|
974
|
+
return [...staleFiles(cwd, compiled.files).map((file) => ({
|
|
857
975
|
code: "PD058",
|
|
858
976
|
severity: "warning",
|
|
859
977
|
message: file.missing ? `${file.path} is missing, so ${file.path === config.powersync?.manifest ? "the device builds no local snapshot" : "PowerSync syncs no stream the policy compiles to"}` : `${file.path} is not what the policy compiles to, so ${file.path === config.powersync?.manifest ? "the device builds snapshots" : "PowerSync syncs rows"} by an older policy`,
|
|
860
978
|
fix: "run permdock powersync generate and deploy the files"
|
|
861
|
-
}));
|
|
979
|
+
})), ...gaps];
|
|
862
980
|
}
|
|
863
981
|
//#endregion
|
|
864
982
|
//#region src/cli/doctor/project.ts
|
|
@@ -889,7 +1007,10 @@ function isWorkspaceRoot(files, dir) {
|
|
|
889
1007
|
return false;
|
|
890
1008
|
}
|
|
891
1009
|
}
|
|
892
|
-
/**
|
|
1010
|
+
/**
|
|
1011
|
+
* Whether `dir` holds a skills lock: PermDock's own, or the `skills` CLI's
|
|
1012
|
+
* naming the `permdock` skill.
|
|
1013
|
+
*/
|
|
893
1014
|
function hasSkillsLock(files, dir) {
|
|
894
1015
|
if (files.exists(join(dir, ".permdock/skills-lock.json"))) return true;
|
|
895
1016
|
const shared = join(dir, "skills-lock.json");
|
|
@@ -994,10 +1115,10 @@ const ALTER_VIEW = new RegExp(String.raw`\balter\s+view\s+(?:if\s+exists\s+)?${V
|
|
|
994
1115
|
const INVOKER = /\bsecurity_invoker\s*(?:=\s*(?:true|on|'true'|'on'|1)\b|[,)]|$)/iu;
|
|
995
1116
|
const COMPANION = new RegExp(String.raw`\bcomment\s+on\s+view\s+${VIEW_NAME}\s+is\s+'${FIELD_VIEWS.comment}\b`, "giu");
|
|
996
1117
|
/**
|
|
997
|
-
* Views run as their owner unless `security_invoker` is set, so they read
|
|
998
|
-
*
|
|
999
|
-
*
|
|
1000
|
-
*
|
|
1118
|
+
* Views run as their owner unless `security_invoker` is set, so they read past
|
|
1119
|
+
* RLS on the tables beneath them. The `<table>_visible_fields` companion `rls
|
|
1120
|
+
* generate --revoke-columns` writes reads as its owner on purpose and carries a
|
|
1121
|
+
* comment that says so.
|
|
1001
1122
|
*/
|
|
1002
1123
|
function pd022(cwd, config, files = nodeFiles) {
|
|
1003
1124
|
if (config.rls === void 0 && config.doctor?.migrations === void 0) return [];
|
|
@@ -1025,9 +1146,9 @@ function pd022(cwd, config, files = nodeFiles) {
|
|
|
1025
1146
|
}
|
|
1026
1147
|
const AUTH_ROLE = /\bauth\.role\s*\(\s*\)/giu;
|
|
1027
1148
|
/**
|
|
1028
|
-
* PD040: `auth.role()` in a migration. Supabase deprecated it; a policy
|
|
1029
|
-
*
|
|
1030
|
-
*
|
|
1149
|
+
* PD040: `auth.role()` in a migration. Supabase deprecated it; a policy names
|
|
1150
|
+
* its Postgres roles with `to authenticated` instead, and the role claim it
|
|
1151
|
+
* reads is the one PostgREST already switched to.
|
|
1031
1152
|
*/
|
|
1032
1153
|
function pd040(cwd, config, files = nodeFiles) {
|
|
1033
1154
|
const findings = [];
|
|
@@ -1064,10 +1185,10 @@ function applyPrivilege(writable, verb, part) {
|
|
|
1064
1185
|
const CREATE_TABLE$1 = /^create\s+(?:(?:global|local)\s+)?(?:(?:temp|temporary|unlogged)\s+)?table\s+(?:if\s+not\s+exists\s+)?((?:"[^"]+"|\w+)(?:\.(?:"[^"]+"|\w+))?)\s*\(/iu;
|
|
1065
1186
|
const REVOKE_DEFAULT_TABLES = /^alter\s+default\s+privileges\s+(?:for\s+(?:role|user)\s+[\w\s,"]+?\s+)?in\s+schema\s+"?public"?\s+revoke\s+[\s\S]+?\s+on\s+tables\s+from\s+[\s\S]*\b(?:anon|authenticated)\b/iu;
|
|
1066
1187
|
/**
|
|
1067
|
-
* Columns of `table` a client role may insert or update, per the migrations
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1070
|
-
*
|
|
1188
|
+
* Columns of `table` a client role may insert or update, per the migrations in
|
|
1189
|
+
* order; `*` for every column. On Supabase a table created in `public` starts
|
|
1190
|
+
* writable: the default privileges grant `anon` and `authenticated` everything,
|
|
1191
|
+
* until a migration revokes them.
|
|
1071
1192
|
*/
|
|
1072
1193
|
function clientWritable(files, cwd, config, target) {
|
|
1073
1194
|
const writable = {
|
|
@@ -1096,7 +1217,10 @@ function clientWritable(files, cwd, config, target) {
|
|
|
1096
1217
|
}
|
|
1097
1218
|
return /* @__PURE__ */ new Set([...writable.insert, ...writable.update]);
|
|
1098
1219
|
}
|
|
1099
|
-
/**
|
|
1220
|
+
/**
|
|
1221
|
+
* Membership columns a client can write: a user who sets `user_id` or `role`
|
|
1222
|
+
* grants themselves access.
|
|
1223
|
+
*/
|
|
1100
1224
|
function membershipFindings(files, cwd, config) {
|
|
1101
1225
|
const findings = [];
|
|
1102
1226
|
for (const [table, columns] of membershipColumns(config)) {
|
|
@@ -1204,7 +1328,10 @@ async function pd020(input) {
|
|
|
1204
1328
|
fix: "remove them from hostable, or drop their tables from rls.tables and enforce them in the application"
|
|
1205
1329
|
}];
|
|
1206
1330
|
}
|
|
1207
|
-
/**
|
|
1331
|
+
/**
|
|
1332
|
+
* Columns of `resource` a field-limited read grant can hide: missing from an
|
|
1333
|
+
* allow's list, or on a deny's.
|
|
1334
|
+
*/
|
|
1208
1335
|
function limitedColumns(policy, resource, actions) {
|
|
1209
1336
|
const reads = policy.grants.filter((grant) => grant.permission.resource === resource && grant.fields !== void 0 && commandFor(grant.permission.action, actions) === "select");
|
|
1210
1337
|
const node = policy.resources.get(resource);
|
|
@@ -1216,8 +1343,8 @@ function limitedColumns(policy, resource, actions) {
|
|
|
1216
1343
|
}
|
|
1217
1344
|
/**
|
|
1218
1345
|
* `fields` redact in the application; RLS is row-level unless field views
|
|
1219
|
-
* compile, and a field view protects nothing while the base table still
|
|
1220
|
-
*
|
|
1346
|
+
* compile, and a field view protects nothing while the base table still returns
|
|
1347
|
+
* the columns to a direct read.
|
|
1221
1348
|
*/
|
|
1222
1349
|
async function pd030(input) {
|
|
1223
1350
|
const rls = input.config.rls;
|
|
@@ -1229,8 +1356,8 @@ async function pd030(input) {
|
|
|
1229
1356
|
for (const resource of resources) {
|
|
1230
1357
|
const columns = limitedColumns(policy, resource, rls.actions);
|
|
1231
1358
|
if (columns.length === 0) continue;
|
|
1232
|
-
const table = tableFor(resource, rls.tables);
|
|
1233
|
-
if (rls.fields
|
|
1359
|
+
const table = tableFor(resource, tableNames(rls.tables));
|
|
1360
|
+
if (rls.fields === void 0) {
|
|
1234
1361
|
findings.push({
|
|
1235
1362
|
code: "PD030",
|
|
1236
1363
|
severity: "warning",
|
|
@@ -1249,9 +1376,9 @@ async function pd030(input) {
|
|
|
1249
1376
|
return findings;
|
|
1250
1377
|
}
|
|
1251
1378
|
/**
|
|
1252
|
-
* A self-parented resource no `through` grant walks, or a `restricted`
|
|
1253
|
-
*
|
|
1254
|
-
*
|
|
1379
|
+
* A self-parented resource no `through` grant walks, or a `restricted` column
|
|
1380
|
+
* no graph grant reaches: the declaration has no effect on any decision, and
|
|
1381
|
+
* `rls generate` keeps no closure for it.
|
|
1255
1382
|
*/
|
|
1256
1383
|
async function pd031(input) {
|
|
1257
1384
|
if (input.config.policy === void 0) return [];
|
|
@@ -1279,8 +1406,8 @@ async function pd031(input) {
|
|
|
1279
1406
|
}
|
|
1280
1407
|
/**
|
|
1281
1408
|
* Under an `rls` config, a graph resource `rls generate` cannot name a helper
|
|
1282
|
-
* for: one that shares a scope's name (its `permitted_<name>_ids` would
|
|
1283
|
-
*
|
|
1409
|
+
* for: one that shares a scope's name (its `permitted_<name>_ids` would replace
|
|
1410
|
+
* the scope helper) or is not a lowercase SQL name.
|
|
1284
1411
|
*/
|
|
1285
1412
|
async function pd032(input) {
|
|
1286
1413
|
if (input.config.rls === void 0 || input.config.policy === void 0) return [];
|
|
@@ -1336,8 +1463,35 @@ async function pd034(input) {
|
|
|
1336
1463
|
}));
|
|
1337
1464
|
}
|
|
1338
1465
|
/**
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1466
|
+
* PD076: `rls.actions` entries set to `'none'` that change nothing: a verb that
|
|
1467
|
+
* is not one of the six default verbs and does not read as a write compiles to
|
|
1468
|
+
* no policy anyway, and a verb no permission uses compiles to nothing at all. A
|
|
1469
|
+
* write-sounding `none` stays, because it silences PD071.
|
|
1470
|
+
*/
|
|
1471
|
+
async function pd076(input) {
|
|
1472
|
+
const actions = input.config.rls?.actions;
|
|
1473
|
+
if (actions === void 0 || input.config.policy === void 0) return [];
|
|
1474
|
+
const policy = await policyOf(input);
|
|
1475
|
+
if (policy === void 0) return [];
|
|
1476
|
+
const used = new Set(listPermissions(policy.vocabulary.permissions).map((leaf) => leaf.action));
|
|
1477
|
+
const unused = Object.keys(actions).filter((verb) => !used.has(verb)).toSorted();
|
|
1478
|
+
const noop = Object.entries(actions).filter(([verb, command]) => command === "none" && used.has(verb) && commandFor(verb) === void 0 && !suggestsWrite(verb)).map(([verb]) => verb).toSorted();
|
|
1479
|
+
return [...noop.length === 0 ? [] : [{
|
|
1480
|
+
code: "PD076",
|
|
1481
|
+
severity: "info",
|
|
1482
|
+
message: `rls.actions sets ${noop.join(", ")} to 'none', which is already what ${noop.length === 1 ? "that verb compiles" : "those verbs compile"} to`,
|
|
1483
|
+
fix: `delete these entries: ${noop.join(", ")}`
|
|
1484
|
+
}], ...unused.length === 0 ? [] : [{
|
|
1485
|
+
code: "PD076",
|
|
1486
|
+
severity: "info",
|
|
1487
|
+
message: `rls.actions maps ${unused.join(", ")}, which no permission uses`,
|
|
1488
|
+
fix: `delete these entries, or fix the verb's spelling: ${unused.join(", ")}`
|
|
1489
|
+
}]];
|
|
1490
|
+
}
|
|
1491
|
+
/**
|
|
1492
|
+
* Storage and Realtime policies written outside PermDock that call the SQL
|
|
1493
|
+
* helpers for a permission whose grants carry row conditions: the helpers check
|
|
1494
|
+
* only role and scope.
|
|
1341
1495
|
*/
|
|
1342
1496
|
async function pd037(input) {
|
|
1343
1497
|
const files = filesOf(input);
|
|
@@ -1374,7 +1528,10 @@ const WRITE_VERB_PREFIXES = [
|
|
|
1374
1528
|
"confirm",
|
|
1375
1529
|
"pause"
|
|
1376
1530
|
];
|
|
1377
|
-
/**
|
|
1531
|
+
/**
|
|
1532
|
+
* `verb` starts with a write prefix that ends at a word boundary: `markPaid`,
|
|
1533
|
+
* not `market`.
|
|
1534
|
+
*/
|
|
1378
1535
|
function suggestsWrite(verb) {
|
|
1379
1536
|
return WRITE_VERB_PREFIXES.some((prefix) => {
|
|
1380
1537
|
if (!verb.startsWith(prefix)) return false;
|
|
@@ -1399,20 +1556,100 @@ async function pd071(input) {
|
|
|
1399
1556
|
fix: `map it on purpose in rls.actions: ${verb}: "insert", "update", "delete" or "none"`
|
|
1400
1557
|
}));
|
|
1401
1558
|
}
|
|
1559
|
+
/**
|
|
1560
|
+
* PD072: a membership source reads role keys through the table of custom roles
|
|
1561
|
+
* (`rls.customRoleWrites.roles`), which tenants write.
|
|
1562
|
+
*/
|
|
1563
|
+
function pd072(input) {
|
|
1564
|
+
const roles = input.config.rls?.customRoleWrites?.roles;
|
|
1565
|
+
if (roles?.tenant === void 0) return [];
|
|
1566
|
+
const target = qualifySqlTable(roles.table);
|
|
1567
|
+
return membershipSourcesOf(input.config).filter((source) => source.sql.throughs.some((through) => through.table === target)).map((source) => ({
|
|
1568
|
+
code: "PD072",
|
|
1569
|
+
severity: "warning",
|
|
1570
|
+
message: `the ${source.sql.table} membership source reads role keys through ${target}, whose tenant rows tenants write: a tenant row named like a declared role must not grant it`,
|
|
1571
|
+
fix: `keep the permdock_custom_role_name_guard trigger on ${target} (rls generate emits it with customRoleWrites.roles) and generate with this config, so helpers read a tenant row named like a declared role as no role`
|
|
1572
|
+
}));
|
|
1573
|
+
}
|
|
1574
|
+
/** PD073: a role's `for` lists kinds no membership source of its scope can hold. */
|
|
1575
|
+
async function pd073(input) {
|
|
1576
|
+
const rls = input.config.rls;
|
|
1577
|
+
if (rls === void 0 || input.config.policy === void 0) return [];
|
|
1578
|
+
const policy = await policyOf(input);
|
|
1579
|
+
if (policy === void 0) return [];
|
|
1580
|
+
const scopes = scopeList(policy.scopes);
|
|
1581
|
+
const ownership = ownershipRules(policy, scopes);
|
|
1582
|
+
const sources = membershipSourcesOf(input.config);
|
|
1583
|
+
const ctx = {
|
|
1584
|
+
dialect: "supabase",
|
|
1585
|
+
scopes,
|
|
1586
|
+
tenantClaim: "tenant_id",
|
|
1587
|
+
gucPrefix: "app",
|
|
1588
|
+
...rls.memberships === void 0 ? {} : { memberships: rls.memberships },
|
|
1589
|
+
...sources === void 0 ? {} : { memberSources: sources },
|
|
1590
|
+
...ownership === void 0 ? {} : { ownership }
|
|
1591
|
+
};
|
|
1592
|
+
return unsatisfiedKinds(ctx, policy).map((item) => ({
|
|
1593
|
+
code: "PD073",
|
|
1594
|
+
severity: "error",
|
|
1595
|
+
message: `role ${item.role} is for ${item.for.join(", ")}, but its membership sources hold ${item.sources.join(", ")}: no membership can hold it`,
|
|
1596
|
+
fix: `set via on one of those sources to ${item.for.join(" or ")}, or change for on role ${item.role}`
|
|
1597
|
+
}));
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* PD075: two resources that `rls.tables` maps to one table, each with its own
|
|
1601
|
+
* keys and helpers for the same rows. `doctor.sharedTables` lists tables split
|
|
1602
|
+
* on purpose (two resources over disjoint rows).
|
|
1603
|
+
*/
|
|
1604
|
+
async function pd075(input) {
|
|
1605
|
+
const rls = input.config.rls;
|
|
1606
|
+
if (rls === void 0 || input.config.policy === void 0) return [];
|
|
1607
|
+
const policy = await policyOf(input);
|
|
1608
|
+
if (policy === void 0) return [];
|
|
1609
|
+
const allowed = new Set((input.config.doctor?.sharedTables ?? []).map(qualifySqlTable));
|
|
1610
|
+
const byTable = /* @__PURE__ */ new Map();
|
|
1611
|
+
for (const leaf of listPermissions(policy.vocabulary.permissions)) {
|
|
1612
|
+
const table = qualifySqlTable(tableNameOf(rls.tables?.[leaf.resource], leaf.resource));
|
|
1613
|
+
byTable.set(table, (byTable.get(table) ?? /* @__PURE__ */ new Set()).add(leaf.resource));
|
|
1614
|
+
}
|
|
1615
|
+
return [...byTable.entries()].filter(([table, resources]) => resources.size > 1 && !allowed.has(table)).toSorted(byKey).map(([table, resources]) => {
|
|
1616
|
+
return {
|
|
1617
|
+
code: "PD075",
|
|
1618
|
+
severity: "warning",
|
|
1619
|
+
message: `rls.tables maps ${[...resources].toSorted().join(" and ")} to ${table}, so the same rows carry two sets of keys and helpers`,
|
|
1620
|
+
fix: `merge them into one resource (https://permdock.com/docs/guides/merge-resources), or list ${table} in doctor.sharedTables when the resources cover disjoint rows`
|
|
1621
|
+
};
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1402
1624
|
//#endregion
|
|
1403
1625
|
//#region src/cli/doctor/seeds.ts
|
|
1404
1626
|
const SEED_INSERT = /^insert\s+into\s+((?:"[^"]+"|\w+)(?:\.(?:"[^"]+"|\w+))?)\s*\(\s*role\s*,\s*permission\s*,\s*grant_key\s*,\s*scope\s*,\s*effect\s*\)\s*values\s*([\s\S]*?)\s*on\s+conflict\b/iu;
|
|
1405
1627
|
const SEED_CLEAR = /^delete\s+from\s+((?:"[^"]+"|\w+)(?:\.(?:"[^"]+"|\w+))?)\s*$/iu;
|
|
1406
1628
|
const LITERAL = String.raw`'((?:[^']|'')*)'`;
|
|
1407
1629
|
const SEED_ROW = new RegExp(String.raw`\(\s*${LITERAL}\s*,\s*${LITERAL}\s*,\s*${LITERAL}\s*,\s*${LITERAL}\s*,\s*${LITERAL}\s*\)`, "gu");
|
|
1630
|
+
const SEED_VIEW = /^create\s+(?:or\s+replace\s+)?view\s+((?:"[^"]+"|\w+)(?:\.(?:"[^"]+"|\w+))?)\s*\([^)]*\)\s*with\s*\([^)]*\)\s*as\s+([\s\S]*)$/iu;
|
|
1631
|
+
const SEED_FUNCTION = /^create\s+(?:or\s+replace\s+)?function\s+(?:"[^"]+"|\w+)\.(?:"seed"|seed)\s*\(\s*\)[\s\S]*?\$(\w*)\$([\s\S]*)\$\1\$/iu;
|
|
1408
1632
|
function rowKey(values) {
|
|
1409
1633
|
return values.join(" ");
|
|
1410
1634
|
}
|
|
1411
|
-
/**
|
|
1635
|
+
/**
|
|
1636
|
+
* The statements of a file, with the body of `<schema>.seed()` (`rls.seeds:
|
|
1637
|
+
* 'function'`) in place of its `create function`.
|
|
1638
|
+
*/
|
|
1639
|
+
function seedStatements(sql) {
|
|
1640
|
+
return sqlStatements(sql).flatMap((statement) => {
|
|
1641
|
+
const fn = SEED_FUNCTION.exec(statement.text);
|
|
1642
|
+
return fn === null ? [statement] : sqlStatements(group(fn, 2));
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* The rows of the last statement that seeds or clears `table`, in migration
|
|
1647
|
+
* order.
|
|
1648
|
+
*/
|
|
1412
1649
|
function lastSeed(files, cwd, config, table) {
|
|
1413
1650
|
let seeded;
|
|
1414
|
-
for (const file of sqlFiles(cwd, config.doctor?.migrations ?? MIGRATION_DIRS)) for (const { text } of
|
|
1415
|
-
const insert = SEED_INSERT.exec(text);
|
|
1651
|
+
for (const file of sqlFiles(cwd, config.doctor?.migrations ?? MIGRATION_DIRS)) for (const { text } of seedStatements(files.read(file))) {
|
|
1652
|
+
const insert = SEED_INSERT.exec(text) ?? SEED_VIEW.exec(text);
|
|
1416
1653
|
if (insert !== null && tableKey(group(insert, 1)) === table) {
|
|
1417
1654
|
const rows = /* @__PURE__ */ new Set();
|
|
1418
1655
|
for (const row of group(insert, 2).matchAll(SEED_ROW)) rows.add(rowKey(row.slice(1, 6).map((value) => value.replaceAll("''", "'"))));
|
|
@@ -1435,8 +1672,8 @@ function sample(rows) {
|
|
|
1435
1672
|
return rows.slice(0, 3).map((row) => `(${row})`).join(", ");
|
|
1436
1673
|
}
|
|
1437
1674
|
/**
|
|
1438
|
-
* PD054: the `role_permissions` rows the migrations seed last are not the
|
|
1439
|
-
*
|
|
1675
|
+
* PD054: the `role_permissions` rows the migrations seed last are not the rows
|
|
1676
|
+
* the policy compiles to, so the helpers answer from a stale policy.
|
|
1440
1677
|
*/
|
|
1441
1678
|
async function pd054(input) {
|
|
1442
1679
|
const { cwd, config } = input;
|
|
@@ -1447,18 +1684,26 @@ async function pd054(input) {
|
|
|
1447
1684
|
if (seeded === void 0) return [];
|
|
1448
1685
|
const policy = await policyOf(input);
|
|
1449
1686
|
if (policy === void 0) return [];
|
|
1450
|
-
const
|
|
1687
|
+
const generate = {
|
|
1688
|
+
cwd,
|
|
1689
|
+
config,
|
|
1690
|
+
target: "sql",
|
|
1451
1691
|
dialect: rls?.dialect ?? "supabase",
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1692
|
+
rbac: false,
|
|
1693
|
+
check: false,
|
|
1694
|
+
skipClosures: true,
|
|
1695
|
+
inlineFunctions: false,
|
|
1696
|
+
io: {
|
|
1697
|
+
stdout: () => void 0,
|
|
1698
|
+
stderr: () => void 0
|
|
1699
|
+
}
|
|
1458
1700
|
};
|
|
1459
1701
|
let expected;
|
|
1460
1702
|
try {
|
|
1461
|
-
|
|
1703
|
+
const options = generateOptions(generate);
|
|
1704
|
+
const built = isRefusal(options) ? options : buildContext(generate, policy, options);
|
|
1705
|
+
if (isRefusal(built)) return [];
|
|
1706
|
+
expected = new Set(compileGrants(policy, built.ctx, tableNames(rls?.tables), [], true, rls?.rowHelpers !== void 0 || inheritTargets(policy).length > 0).rolePermissions.map((row) => rowKey([
|
|
1462
1707
|
row.role,
|
|
1463
1708
|
row.permission,
|
|
1464
1709
|
row.grantKey,
|
|
@@ -1518,7 +1763,10 @@ function pd046(cwd, config, files = nodeFiles) {
|
|
|
1518
1763
|
}];
|
|
1519
1764
|
}
|
|
1520
1765
|
const USER_METADATA = /\b(raw_user_meta_data|user_metadata)\b/giu;
|
|
1521
|
-
/**
|
|
1766
|
+
/**
|
|
1767
|
+
* PD047: SQL that reads `user_metadata`, which every user can edit for
|
|
1768
|
+
* themselves.
|
|
1769
|
+
*/
|
|
1522
1770
|
function pd047(cwd, config, files = nodeFiles) {
|
|
1523
1771
|
if (!isSupabase(files, cwd, config)) return [];
|
|
1524
1772
|
return migrationStatements(files, cwd, config).flatMap((statement) => [...statement.text.matchAll(USER_METADATA)].map((match) => ({
|
|
@@ -1542,7 +1790,10 @@ function createdFunctions(statements) {
|
|
|
1542
1790
|
}];
|
|
1543
1791
|
});
|
|
1544
1792
|
}
|
|
1545
|
-
/**
|
|
1793
|
+
/**
|
|
1794
|
+
* PD048: a function without `set search_path`, so a caller's objects can shadow
|
|
1795
|
+
* the ones it names.
|
|
1796
|
+
*/
|
|
1546
1797
|
function pd048(cwd, config, files = nodeFiles) {
|
|
1547
1798
|
if (!isSupabase(files, cwd, config)) return [];
|
|
1548
1799
|
const statements = migrationStatements(files, cwd, config);
|
|
@@ -1560,7 +1811,10 @@ function pd048(cwd, config, files = nodeFiles) {
|
|
|
1560
1811
|
};
|
|
1561
1812
|
});
|
|
1562
1813
|
}
|
|
1563
|
-
/**
|
|
1814
|
+
/**
|
|
1815
|
+
* Roles each `revoke` statement takes `execute` from, per function key or
|
|
1816
|
+
* `schema.*`.
|
|
1817
|
+
*/
|
|
1564
1818
|
function revokedExecute(statements) {
|
|
1565
1819
|
const revoked = /* @__PURE__ */ new Map();
|
|
1566
1820
|
const add = (key, roles) => {
|
|
@@ -1583,24 +1837,53 @@ function revokedExecute(statements) {
|
|
|
1583
1837
|
return revoked;
|
|
1584
1838
|
}
|
|
1585
1839
|
/**
|
|
1840
|
+
* `supabase.hook.claims` and `hook.before` functions, by key, with the argument
|
|
1841
|
+
* type the hook calls them with.
|
|
1842
|
+
*/
|
|
1843
|
+
function hookClaimFunctions(config) {
|
|
1844
|
+
const hook = config.supabase?.hook;
|
|
1845
|
+
const before = hook?.before;
|
|
1846
|
+
const functions = /* @__PURE__ */ new Map();
|
|
1847
|
+
for (const fn of Object.values(hook?.claims ?? {})) if (typeof fn === "string") functions.set(tableKey(fn), "uuid");
|
|
1848
|
+
for (const fn of typeof before === "string" ? [before] : before ?? []) if (typeof fn === "string") functions.set(tableKey(fn), "jsonb");
|
|
1849
|
+
return functions;
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1586
1852
|
* PD049: a `security definer` function `public` (every role) or, in Supabase's
|
|
1587
1853
|
* `public` schema, `anon` may still execute: Postgres grants `execute` to
|
|
1588
|
-
* `public` on create, and Supabase's default privileges grant it to `anon`.
|
|
1854
|
+
* `public` on create, and Supabase's default privileges grant it to `anon`. A
|
|
1855
|
+
* hook claim function returns any user's claims, so `authenticated` must not
|
|
1856
|
+
* execute it either, definer or not.
|
|
1589
1857
|
*/
|
|
1590
1858
|
function pd049(cwd, config, files = nodeFiles) {
|
|
1591
1859
|
if (!isSupabase(files, cwd, config)) return [];
|
|
1592
1860
|
const statements = migrationStatements(files, cwd, config);
|
|
1593
1861
|
const revoked = revokedExecute(statements);
|
|
1862
|
+
const hookFunctions = hookClaimFunctions(config);
|
|
1594
1863
|
const findings = [];
|
|
1595
1864
|
for (const fn of createdFunctions(statements)) {
|
|
1596
|
-
|
|
1865
|
+
const hookArgument = hookFunctions.get(fn.key);
|
|
1866
|
+
if (hookArgument === void 0 && (!/\bsecurity\s+definer\b/iu.test(fn.head) || /\breturns\s+(?:event_)?trigger\b/iu.test(fn.head))) continue;
|
|
1597
1867
|
const schema = schemaOf(fn.key);
|
|
1598
1868
|
const from = /* @__PURE__ */ new Set([
|
|
1599
1869
|
...revoked.get(fn.key) ?? [],
|
|
1600
1870
|
...revoked.get(`${schema}.*`) ?? [],
|
|
1601
1871
|
...revoked.get("*.*") ?? []
|
|
1602
1872
|
]);
|
|
1603
|
-
const missing = ["public", ...schema === "public" ? ["anon"] : []]
|
|
1873
|
+
const missing = (hookArgument === void 0 ? ["public", ...schema === "public" ? ["anon"] : []] : [
|
|
1874
|
+
"public",
|
|
1875
|
+
"anon",
|
|
1876
|
+
"authenticated"
|
|
1877
|
+
]).filter((role) => !from.has(role));
|
|
1878
|
+
if (missing.length > 0 && hookArgument !== void 0) {
|
|
1879
|
+
findings.push({
|
|
1880
|
+
code: "PD049",
|
|
1881
|
+
severity: "warning",
|
|
1882
|
+
message: `${fn.file}:${String(fn.line)} hook claim function ${fn.key} returns any user's claims and can be executed by ${missing.join(" and ")}`,
|
|
1883
|
+
fix: `regenerate the hook (permdock rls generate), which revokes it, or add revoke all on function ${fn.key}(${hookArgument}) from public, anon, authenticated;`
|
|
1884
|
+
});
|
|
1885
|
+
continue;
|
|
1886
|
+
}
|
|
1604
1887
|
if (missing.length > 0) findings.push({
|
|
1605
1888
|
code: "PD049",
|
|
1606
1889
|
severity: "warning",
|
|
@@ -1613,7 +1896,10 @@ function pd049(cwd, config, files = nodeFiles) {
|
|
|
1613
1896
|
const CREATE_TABLE = new RegExp(String.raw`^create\s+(?:(?:global|local)\s+)?(?:(?:temp|temporary|unlogged)\s+)?table\s+(?:if\s+not\s+exists\s+)?${NAME}\s*\(`, "iu");
|
|
1614
1897
|
const ENABLE_RLS = new RegExp(String.raw`^alter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?${NAME}[\s\S]*\benable\s+row\s+level\s+security\b`, "iu");
|
|
1615
1898
|
const DROP_TABLE = /^drop\s+table\s+(?:if\s+exists\s+)?([\s\S]+?)(?:\s+(?:cascade|restrict))?$/iu;
|
|
1616
|
-
/**
|
|
1899
|
+
/**
|
|
1900
|
+
* PD050: a `public` table without row level security, which the Data API serves
|
|
1901
|
+
* whole.
|
|
1902
|
+
*/
|
|
1617
1903
|
function pd050(cwd, config, files = nodeFiles) {
|
|
1618
1904
|
if (!isSupabase(files, cwd, config)) return [];
|
|
1619
1905
|
const created = /* @__PURE__ */ new Map();
|
|
@@ -1641,7 +1927,10 @@ function pd050(cwd, config, files = nodeFiles) {
|
|
|
1641
1927
|
}
|
|
1642
1928
|
const CREATE_POLICY = new RegExp(String.raw`^create\s+policy\s+${IDENT}\s+on\s+${NAME}([\s\S]*)$`, "iu");
|
|
1643
1929
|
const UNWRAPPED_AUTH = /(?<!\bselect\s{1,20})\bauth\.(uid|jwt)\s*\(\s*\)/giu;
|
|
1644
|
-
/**
|
|
1930
|
+
/**
|
|
1931
|
+
* PD051: a policy that calls `auth.uid()` or `auth.jwt()` bare, so Postgres
|
|
1932
|
+
* runs it once per row.
|
|
1933
|
+
*/
|
|
1645
1934
|
function pd051(cwd, config, files = nodeFiles) {
|
|
1646
1935
|
if (!isSupabase(files, cwd, config)) return [];
|
|
1647
1936
|
return migrationStatements(files, cwd, config).flatMap((statement) => {
|
|
@@ -1657,7 +1946,10 @@ function pd051(cwd, config, files = nodeFiles) {
|
|
|
1657
1946
|
}];
|
|
1658
1947
|
});
|
|
1659
1948
|
}
|
|
1660
|
-
/**
|
|
1949
|
+
/**
|
|
1950
|
+
* PD052: an update policy without `with check`, so nothing states what a row
|
|
1951
|
+
* may become.
|
|
1952
|
+
*/
|
|
1661
1953
|
function pd052(cwd, config, files = nodeFiles) {
|
|
1662
1954
|
if (!isSupabase(files, cwd, config)) return [];
|
|
1663
1955
|
return migrationStatements(files, cwd, config).flatMap((statement) => {
|
|
@@ -1690,9 +1982,9 @@ function topLevelParts(body) {
|
|
|
1690
1982
|
return parts.filter((part) => part !== "");
|
|
1691
1983
|
}
|
|
1692
1984
|
/**
|
|
1693
|
-
* One column definition or table constraint of a `create table` body: its
|
|
1694
|
-
*
|
|
1695
|
-
*
|
|
1985
|
+
* One column definition or table constraint of a `create table` body: its first
|
|
1986
|
+
* column, whether it is a foreign key, and whether a primary key or unique
|
|
1987
|
+
* constraint indexes that column.
|
|
1696
1988
|
*/
|
|
1697
1989
|
function tablePart(raw) {
|
|
1698
1990
|
const part = raw.replace(/^constraint\s+(?:"[^"]+"|\w+)\s+/iu, "");
|
|
@@ -1716,7 +2008,10 @@ function firstColumn(list) {
|
|
|
1716
2008
|
}
|
|
1717
2009
|
const ADD_CONSTRAINT = new RegExp(String.raw`^alter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?${NAME}\s+add\s+(?:constraint\s+${IDENT}\s+)?(foreign\s+key|primary\s+key|unique)\s*\(([^)]*)\)`, "iu");
|
|
1718
2010
|
const CREATE_INDEX = new RegExp(String.raw`^create\s+(?:unique\s+)?index\s+(?:concurrently\s+)?(?:if\s+not\s+exists\s+)?(?:${IDENT}\s+)?on\s+(?:only\s+)?${NAME}\s*(?:using\s+\w+\s*)?\(\s*${IDENT}`, "iu");
|
|
1719
|
-
/**
|
|
2011
|
+
/**
|
|
2012
|
+
* PD053: a foreign key no index starts with, so joins and deletes on the
|
|
2013
|
+
* referenced row scan the table.
|
|
2014
|
+
*/
|
|
1720
2015
|
function pd053(cwd, config, files = nodeFiles) {
|
|
1721
2016
|
if (!isSupabase(files, cwd, config)) return [];
|
|
1722
2017
|
const keys = [];
|
|
@@ -1765,10 +2060,10 @@ function at(statement) {
|
|
|
1765
2060
|
return `${statement.file}:${String(statement.line)}`;
|
|
1766
2061
|
}
|
|
1767
2062
|
/**
|
|
1768
|
-
* PD062: SQL that reads or sets a per-claim `request.jwt.claim.<name>`
|
|
1769
|
-
*
|
|
1770
|
-
*
|
|
1771
|
-
*
|
|
2063
|
+
* PD062: SQL that reads or sets a per-claim `request.jwt.claim.<name>` setting.
|
|
2064
|
+
* Current PostgREST, `withPostgresClient`, PermDock's `withSubject` and `rls
|
|
2065
|
+
* verify` set only `request.jwt.claims`. A function replaced by a later
|
|
2066
|
+
* migration is judged by its last definition.
|
|
1772
2067
|
*/
|
|
1773
2068
|
function pd062(cwd, config, files = nodeFiles) {
|
|
1774
2069
|
if (!isSupabase(files, cwd, config)) return [];
|
|
@@ -1787,8 +2082,8 @@ function pd062(cwd, config, files = nodeFiles) {
|
|
|
1787
2082
|
});
|
|
1788
2083
|
}
|
|
1789
2084
|
/**
|
|
1790
|
-
* PD063: a migration statement supautils rejects for `postgres`, so
|
|
1791
|
-
*
|
|
2085
|
+
* PD063: a migration statement supautils rejects for `postgres`, so `supabase
|
|
2086
|
+
* db push` fails on it.
|
|
1792
2087
|
*/
|
|
1793
2088
|
function pd063(cwd, config, files = nodeFiles) {
|
|
1794
2089
|
if (!isSupabase(files, cwd, config)) return [];
|
|
@@ -1846,9 +2141,9 @@ function pd066(cwd, config, files = nodeFiles) {
|
|
|
1846
2141
|
//#endregion
|
|
1847
2142
|
//#region src/cli/doctor/suspension.ts
|
|
1848
2143
|
/**
|
|
1849
|
-
* PD061: a suspended scope keeps permissions. Each scope's kept keys are
|
|
1850
|
-
*
|
|
1851
|
-
*
|
|
2144
|
+
* PD061: a suspended scope keeps permissions. Each scope's kept keys are listed
|
|
2145
|
+
* for review, since its members hold them while the instance is suspended, and
|
|
2146
|
+
* a key the definitions do not declare is named apart.
|
|
1852
2147
|
*/
|
|
1853
2148
|
async function pd061(input) {
|
|
1854
2149
|
const suspension = input.config.rls?.suspension;
|
|
@@ -2078,6 +2373,31 @@ const DOCTOR_CHECKS = [
|
|
|
2078
2373
|
groups: ["rls"],
|
|
2079
2374
|
run: pd071
|
|
2080
2375
|
},
|
|
2376
|
+
{
|
|
2377
|
+
code: "PD072",
|
|
2378
|
+
groups: ["rls"],
|
|
2379
|
+
run: pd072
|
|
2380
|
+
},
|
|
2381
|
+
{
|
|
2382
|
+
code: "PD073",
|
|
2383
|
+
groups: ["rls", "ownership"],
|
|
2384
|
+
run: pd073
|
|
2385
|
+
},
|
|
2386
|
+
{
|
|
2387
|
+
code: "PD075",
|
|
2388
|
+
groups: ["rls"],
|
|
2389
|
+
run: pd075
|
|
2390
|
+
},
|
|
2391
|
+
{
|
|
2392
|
+
code: "PD076",
|
|
2393
|
+
groups: ["rls"],
|
|
2394
|
+
run: pd076
|
|
2395
|
+
},
|
|
2396
|
+
{
|
|
2397
|
+
code: "PD077",
|
|
2398
|
+
groups: ["rls", "scopes"],
|
|
2399
|
+
run: pd077
|
|
2400
|
+
},
|
|
2081
2401
|
{
|
|
2082
2402
|
code: "PD028",
|
|
2083
2403
|
groups: ["attrs", "supabase"],
|
|
@@ -2181,6 +2501,12 @@ const DOCTOR_CHECKS = [
|
|
|
2181
2501
|
groups: ["supabase"],
|
|
2182
2502
|
run: (project) => pd045(project.cwd)
|
|
2183
2503
|
},
|
|
2504
|
+
{
|
|
2505
|
+
code: "PD074",
|
|
2506
|
+
groups: ["supabase", "helpers"],
|
|
2507
|
+
when: (config) => config.supabase?.hook !== void 0,
|
|
2508
|
+
run: (project) => pd074(project.cwd, project.config)
|
|
2509
|
+
},
|
|
2184
2510
|
...[
|
|
2185
2511
|
["PD046", pd046],
|
|
2186
2512
|
["PD047", pd047],
|
|
@@ -2295,7 +2621,10 @@ async function runDoctor(input) {
|
|
|
2295
2621
|
}))
|
|
2296
2622
|
};
|
|
2297
2623
|
}
|
|
2298
|
-
/**
|
|
2624
|
+
/**
|
|
2625
|
+
* PD059: the configured policy module throws or exports no policy, so every
|
|
2626
|
+
* policy check is skipped.
|
|
2627
|
+
*/
|
|
2299
2628
|
async function pd059(project) {
|
|
2300
2629
|
const load = await project.policyLoad();
|
|
2301
2630
|
if (load.status !== "failed") return [];
|
|
@@ -2306,7 +2635,10 @@ async function pd059(project) {
|
|
|
2306
2635
|
fix: "fix the module so it exports policy from definePolicy, or point policy in permdock.config.ts at the module that does"
|
|
2307
2636
|
}];
|
|
2308
2637
|
}
|
|
2309
|
-
/**
|
|
2638
|
+
/**
|
|
2639
|
+
* PD060: a source file under srcPath has a syntax error, so collect and usage
|
|
2640
|
+
* miss what it references.
|
|
2641
|
+
*/
|
|
2310
2642
|
async function pd060(project) {
|
|
2311
2643
|
return ((await project.collected()).scan?.unparsed ?? []).map((item) => ({
|
|
2312
2644
|
code: "PD060",
|
|
@@ -2315,7 +2647,10 @@ async function pd060(project) {
|
|
|
2315
2647
|
fix: "fix the syntax error, or exclude the file from collect.srcPath"
|
|
2316
2648
|
}));
|
|
2317
2649
|
}
|
|
2318
|
-
/**
|
|
2650
|
+
/**
|
|
2651
|
+
* A usage message for the first `--only` value that names no check code or
|
|
2652
|
+
* group.
|
|
2653
|
+
*/
|
|
2319
2654
|
function unknownOnly(wanted) {
|
|
2320
2655
|
const known = /* @__PURE__ */ new Set();
|
|
2321
2656
|
for (const check of DOCTOR_CHECKS) {
|
|
@@ -2333,15 +2668,24 @@ function formatDoctor(report, color) {
|
|
|
2333
2668
|
for (const change of report.fixed) lines.push(` ${style.okMark} fixed ${change}`);
|
|
2334
2669
|
if (report.fixed.length > 0) lines.push("");
|
|
2335
2670
|
for (const finding of report.findings) {
|
|
2336
|
-
const mark = finding.severity
|
|
2671
|
+
const mark = markOf(finding.severity, style);
|
|
2337
2672
|
lines.push(` ${mark} ${style.paint("bold", finding.code)} ${finding.message}`);
|
|
2338
2673
|
lines.push(` ${style.paint("dim", `fix: ${finding.fix}`)}`);
|
|
2339
2674
|
}
|
|
2340
2675
|
if (report.findings.length === 0) lines.push(" no findings");
|
|
2341
2676
|
lines.push("");
|
|
2342
|
-
|
|
2677
|
+
const notes = report.findings.filter((finding) => finding.severity === "info").length;
|
|
2678
|
+
lines.push(` ${String(report.errors)} error${report.errors === 1 ? "" : "s"}, ${String(report.warnings)} warning${report.warnings === 1 ? "" : "s"}${notes === 0 ? "" : `, ${String(notes)} note${notes === 1 ? "" : "s"}`}`);
|
|
2343
2679
|
return `${lines.join("\n")}\n`;
|
|
2344
2680
|
}
|
|
2681
|
+
function markOf(severity, style) {
|
|
2682
|
+
switch (severity) {
|
|
2683
|
+
case "error": return style.errorMark;
|
|
2684
|
+
case "warning": return style.warnMark;
|
|
2685
|
+
case "info": return style.infoMark;
|
|
2686
|
+
default: return severity;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2345
2689
|
async function supabaseSetup(input) {
|
|
2346
2690
|
const policy = await input.policy();
|
|
2347
2691
|
if (policy === void 0) return [];
|