@jmruthers/pace-core 0.6.4 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +5 -403
  3. package/core-usage-manifest.json +93 -0
  4. package/cursor-rules/00-pace-core-compliance.mdc +128 -26
  5. package/cursor-rules/01-standards-compliance.mdc +49 -8
  6. package/cursor-rules/02-project-structure.mdc +6 -0
  7. package/cursor-rules/03-solid-principles.mdc +2 -0
  8. package/cursor-rules/04-testing-standards.mdc +2 -0
  9. package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
  10. package/cursor-rules/06-code-quality.mdc +2 -0
  11. package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
  12. package/cursor-rules/08-markup-quality.mdc +52 -27
  13. package/cursor-rules/09-rbac-compliance.mdc +462 -0
  14. package/cursor-rules/10-error-handling-patterns.mdc +179 -0
  15. package/cursor-rules/11-performance-optimization.mdc +169 -0
  16. package/cursor-rules/12-ci-cd-integration.mdc +150 -0
  17. package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
  18. package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
  19. package/dist/DataTable-LRJL4IRV.js +15 -0
  20. package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
  21. package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
  22. package/dist/api-Y4MQWOFW.js +4 -0
  23. package/dist/audit-MYQXYZFU.js +3 -0
  24. package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
  25. package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
  26. package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
  27. package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
  28. package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
  29. package/dist/chunk-4T7OBVTU.js +62 -0
  30. package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
  31. package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
  32. package/dist/{chunk-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
  33. package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
  34. package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
  35. package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
  36. package/dist/chunk-C7NSAPTL.js +1 -0
  37. package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
  38. package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
  39. package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
  40. package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
  41. package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
  42. package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
  43. package/dist/chunk-MBADTM7L.js +64 -0
  44. package/dist/chunk-OHIK3MIO.js +994 -0
  45. package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
  46. package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
  47. package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
  48. package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
  49. package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
  50. package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
  51. package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
  52. package/dist/components.d.ts +6 -6
  53. package/dist/components.js +57 -267
  54. package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
  55. package/dist/eslint-rules/index.cjs +22 -0
  56. package/dist/eslint-rules/rules/compliance.cjs +348 -0
  57. package/dist/eslint-rules/rules/components.cjs +113 -0
  58. package/dist/eslint-rules/rules/imports.cjs +102 -0
  59. package/dist/eslint-rules/rules/rbac.cjs +790 -0
  60. package/dist/eslint-rules/utils/helpers.cjs +42 -0
  61. package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
  62. package/dist/hooks.d.ts +5 -5
  63. package/dist/hooks.js +62 -270
  64. package/dist/icons/index.d.ts +1 -0
  65. package/dist/icons/index.js +1 -0
  66. package/dist/index.d.ts +36 -26
  67. package/dist/index.js +87 -690
  68. package/dist/providers.d.ts +2 -2
  69. package/dist/providers.js +8 -35
  70. package/dist/rbac/eslint-rules.d.ts +46 -44
  71. package/dist/rbac/eslint-rules.js +7 -4
  72. package/dist/rbac/index.d.ts +124 -594
  73. package/dist/rbac/index.js +14 -207
  74. package/dist/styles/index.js +2 -12
  75. package/dist/theming/runtime.js +3 -19
  76. package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
  77. package/dist/{types-CkbwOr4Y.d.ts → types-B-K_5VnO.d.ts} +4 -0
  78. package/dist/types-t9H8qKRw.d.ts +55 -0
  79. package/dist/types.d.ts +1 -1
  80. package/dist/types.js +7 -94
  81. package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
  82. package/dist/utils.d.ts +24 -117
  83. package/dist/utils.js +54 -392
  84. package/docs/README.md +16 -6
  85. package/docs/api/README.md +4 -402
  86. package/docs/api/modules.md +454 -930
  87. package/docs/api-reference/components.md +3 -1
  88. package/docs/api-reference/deprecated.md +31 -6
  89. package/docs/api-reference/rpc-functions.md +78 -3
  90. package/docs/best-practices/accessibility.md +6 -3
  91. package/docs/getting-started/cursor-rules.md +3 -23
  92. package/docs/getting-started/dependencies.md +650 -0
  93. package/docs/getting-started/installation-guide.md +20 -7
  94. package/docs/getting-started/quick-start.md +23 -12
  95. package/docs/implementation-guides/permission-enforcement.md +4 -0
  96. package/docs/rbac/MIGRATION_GUIDE.md +819 -0
  97. package/docs/rbac/RBAC_CONTRACT.md +724 -0
  98. package/docs/rbac/README.md +12 -3
  99. package/docs/rbac/edge-functions-guide.md +376 -0
  100. package/docs/rbac/secure-client-protection.md +0 -34
  101. package/docs/standards/00-pace-core-compliance.md +967 -0
  102. package/docs/standards/01-standards-compliance.md +188 -0
  103. package/docs/standards/02-project-structure.md +985 -0
  104. package/docs/standards/03-solid-principles.md +39 -0
  105. package/docs/standards/04-testing-standards.md +36 -0
  106. package/docs/standards/05-bug-reports-and-features.md +27 -0
  107. package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
  108. package/docs/standards/07-tech-stack-compliance.md +30 -0
  109. package/docs/standards/08-markup-quality.md +345 -0
  110. package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
  111. package/docs/standards/10-error-handling-patterns.md +401 -0
  112. package/docs/standards/11-performance-optimization.md +348 -0
  113. package/docs/standards/12-ci-cd-integration.md +370 -0
  114. package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
  115. package/docs/standards/README.md +62 -33
  116. package/docs/troubleshooting/organisation-context-setup.md +42 -19
  117. package/eslint-config-pace-core.cjs +20 -4
  118. package/package.json +31 -21
  119. package/scripts/audit/audit-compliance.cjs +1295 -0
  120. package/scripts/audit/audit-components.cjs +260 -0
  121. package/scripts/audit/audit-dependencies.cjs +395 -0
  122. package/scripts/audit/audit-rbac.cjs +954 -0
  123. package/scripts/audit/audit-standards.cjs +1268 -0
  124. package/scripts/audit/index.cjs +1898 -194
  125. package/scripts/install-cursor-rules.cjs +259 -8
  126. package/scripts/validate-master.js +1 -1
  127. package/src/__tests__/fixtures/supabase.ts +1 -1
  128. package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
  129. package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
  130. package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
  131. package/src/__tests__/helpers/__tests__/test-utils.test.tsx +3 -3
  132. package/src/__tests__/helpers/component-test-utils.tsx +1 -1
  133. package/src/__tests__/helpers/supabaseMock.ts +2 -2
  134. package/src/__tests__/public-recipe-view.test.ts +38 -9
  135. package/src/components/Button/Button.tsx +5 -1
  136. package/src/components/ContextSelector/ContextSelector.tsx +42 -39
  137. package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
  138. package/src/components/DataTable/components/DataTableBody.tsx +55 -31
  139. package/src/components/DataTable/components/DataTableCore.tsx +186 -13
  140. package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
  141. package/src/components/DataTable/components/EditFields.tsx +23 -3
  142. package/src/components/DataTable/components/EditableRow.tsx +7 -2
  143. package/src/components/DataTable/components/ImportModal.tsx +4 -6
  144. package/src/components/DataTable/components/RowComponent.tsx +12 -0
  145. package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
  146. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
  147. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
  148. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
  149. package/src/components/DataTable/core/DataTableContext.tsx +1 -1
  150. package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
  151. package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
  152. package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
  153. package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
  154. package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
  155. package/src/components/DataTable/types.ts +5 -0
  156. package/src/components/DateTimeField/DateTimeField.tsx +20 -20
  157. package/src/components/DateTimeField/README.md +5 -2
  158. package/src/components/Dialog/Dialog.test.tsx +361 -318
  159. package/src/components/Dialog/Dialog.tsx +1154 -323
  160. package/src/components/Dialog/index.ts +3 -3
  161. package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
  162. package/src/components/FileDisplay/FileDisplay.tsx +28 -22
  163. package/src/components/Form/Form.test.tsx +9 -10
  164. package/src/components/Form/Form.tsx +369 -9
  165. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
  166. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
  167. package/src/components/LoginForm/LoginForm.tsx +2 -2
  168. package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
  169. package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
  170. package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
  171. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
  172. package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
  173. package/src/components/PaceAppLayout/README.md +10 -9
  174. package/src/components/PaceAppLayout/test-setup.tsx +40 -31
  175. package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
  176. package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
  177. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
  178. package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
  179. package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
  180. package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
  181. package/src/components/Select/Select.tsx +23 -21
  182. package/src/components/Select/types.ts +1 -1
  183. package/src/components/UserMenu/UserMenu.test.tsx +38 -6
  184. package/src/components/UserMenu/UserMenu.tsx +39 -34
  185. package/src/components/index.ts +3 -4
  186. package/src/eslint-rules/index.cjs +22 -0
  187. package/src/eslint-rules/rules/compliance.cjs +348 -0
  188. package/src/eslint-rules/rules/components.cjs +113 -0
  189. package/src/eslint-rules/rules/imports.cjs +102 -0
  190. package/src/eslint-rules/rules/rbac.cjs +790 -0
  191. package/src/eslint-rules/utils/helpers.cjs +42 -0
  192. package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
  193. package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
  194. package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
  195. package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
  196. package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
  197. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
  198. package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
  199. package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
  200. package/src/hooks/public/usePublicEvent.ts +62 -190
  201. package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
  202. package/src/hooks/public/usePublicEventLogo.ts +19 -9
  203. package/src/hooks/useAppConfig.ts +26 -24
  204. package/src/hooks/useEventTheme.test.ts +211 -233
  205. package/src/hooks/useEventTheme.ts +19 -28
  206. package/src/hooks/useEvents.ts +11 -7
  207. package/src/hooks/useKeyboardShortcuts.ts +1 -1
  208. package/src/hooks/useOrganisationPermissions.ts +9 -11
  209. package/src/hooks/useOrganisations.ts +13 -7
  210. package/src/hooks/useQueryCache.ts +0 -1
  211. package/src/hooks/useSessionDraft.ts +380 -0
  212. package/src/hooks/useSessionRestoration.ts +3 -1
  213. package/src/icons/index.ts +27 -0
  214. package/src/index.ts +16 -1
  215. package/src/providers/OrganisationProvider.tsx +23 -14
  216. package/src/providers/services/EventServiceProvider.tsx +1 -24
  217. package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
  218. package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
  219. package/src/rbac/README.md +20 -20
  220. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
  221. package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
  222. package/src/rbac/adapters.tsx +7 -295
  223. package/src/rbac/api.test.ts +44 -56
  224. package/src/rbac/api.ts +10 -17
  225. package/src/rbac/cache-invalidation.ts +0 -1
  226. package/src/rbac/compliance/index.ts +10 -0
  227. package/src/rbac/compliance/pattern-detector.ts +553 -0
  228. package/src/rbac/compliance/runtime-compliance.ts +22 -0
  229. package/src/rbac/components/AccessDenied.tsx +150 -0
  230. package/src/rbac/components/NavigationGuard.tsx +12 -20
  231. package/src/rbac/components/PagePermissionGuard.tsx +4 -24
  232. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
  233. package/src/rbac/components/index.ts +3 -41
  234. package/src/rbac/eslint-rules.js +1 -1
  235. package/src/rbac/hooks/index.ts +0 -3
  236. package/src/rbac/hooks/permissions/index.ts +0 -3
  237. package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
  238. package/src/rbac/hooks/usePermissions.ts +0 -3
  239. package/src/rbac/hooks/useRBAC.test.ts +21 -3
  240. package/src/rbac/hooks/useRBAC.ts +4 -3
  241. package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
  242. package/src/rbac/hooks/useResolvedScope.ts +58 -140
  243. package/src/rbac/hooks/useResourcePermissions.test.ts +241 -60
  244. package/src/rbac/hooks/useResourcePermissions.ts +182 -63
  245. package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
  246. package/src/rbac/hooks/useRoleManagement.ts +147 -19
  247. package/src/rbac/hooks/useSecureSupabase.ts +4 -8
  248. package/src/rbac/index.ts +7 -9
  249. package/src/rbac/permissions.ts +17 -17
  250. package/src/rbac/utils/contextValidator.ts +45 -7
  251. package/src/services/AuthService.ts +132 -23
  252. package/src/services/EventService.ts +4 -97
  253. package/src/services/InactivityService.ts +155 -58
  254. package/src/services/OrganisationService.ts +7 -44
  255. package/src/services/__tests__/OrganisationService.test.ts +26 -8
  256. package/src/services/base/BaseService.ts +0 -3
  257. package/src/styles/core.css +4 -0
  258. package/src/types/database.generated.ts +4733 -3809
  259. package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
  260. package/src/utils/context/organisationContext.test.ts +13 -28
  261. package/src/utils/context/organisationContext.ts +21 -52
  262. package/src/utils/dynamic/dynamicUtils.ts +1 -1
  263. package/src/utils/file-reference/index.ts +39 -15
  264. package/src/utils/formatting/formatDateTime.test.ts +3 -2
  265. package/src/utils/formatting/formatTime.test.ts +3 -2
  266. package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
  267. package/src/utils/index.ts +4 -1
  268. package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
  269. package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
  270. package/src/utils/persistence/keyDerivation.ts +304 -0
  271. package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
  272. package/src/utils/security/secureStorage.ts +5 -5
  273. package/src/utils/storage/helpers.ts +3 -3
  274. package/src/utils/supabase/createBaseClient.ts +147 -0
  275. package/src/utils/timezone/timezone.test.ts +1 -2
  276. package/src/utils/timezone/timezone.ts +1 -1
  277. package/src/utils/validation/csrf.ts +4 -4
  278. package/cursor-rules/CHANGELOG.md +0 -119
  279. package/cursor-rules/README.md +0 -192
  280. package/dist/DataTable-E7YQZD7D.js +0 -175
  281. package/dist/DataTable-E7YQZD7D.js.map +0 -1
  282. package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
  283. package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
  284. package/dist/api-6LVZTHDS.js +0 -52
  285. package/dist/api-6LVZTHDS.js.map +0 -1
  286. package/dist/audit-V53FV5AG.js +0 -17
  287. package/dist/audit-V53FV5AG.js.map +0 -1
  288. package/dist/chunk-36LVWXB2.js +0 -227
  289. package/dist/chunk-36LVWXB2.js.map +0 -1
  290. package/dist/chunk-3LPHPB62.js.map +0 -1
  291. package/dist/chunk-5DRSZLL2.js.map +0 -1
  292. package/dist/chunk-5EC5MEWX.js.map +0 -1
  293. package/dist/chunk-63FOKYGO.js.map +0 -1
  294. package/dist/chunk-6SOIHG6Z.js.map +0 -1
  295. package/dist/chunk-7JPAB3T5.js.map +0 -1
  296. package/dist/chunk-ATKZM7RX.js +0 -2053
  297. package/dist/chunk-ATKZM7RX.js.map +0 -1
  298. package/dist/chunk-AVMLPIM7.js.map +0 -1
  299. package/dist/chunk-DGUM43GV.js.map +0 -1
  300. package/dist/chunk-E66EQZE6.js.map +0 -1
  301. package/dist/chunk-FFQEQTNW.js.map +0 -1
  302. package/dist/chunk-FMUCXFII.js.map +0 -1
  303. package/dist/chunk-G37KK66H.js.map +0 -1
  304. package/dist/chunk-I6DAQMWX.js.map +0 -1
  305. package/dist/chunk-J36DSWQK.js.map +0 -1
  306. package/dist/chunk-KQCRWDSA.js +0 -1
  307. package/dist/chunk-KQCRWDSA.js.map +0 -1
  308. package/dist/chunk-L4OXEN46.js.map +0 -1
  309. package/dist/chunk-LMC26NLJ.js +0 -84
  310. package/dist/chunk-LMC26NLJ.js.map +0 -1
  311. package/dist/chunk-M43Y4SSO.js.map +0 -1
  312. package/dist/chunk-M7MPQISP.js.map +0 -1
  313. package/dist/chunk-NN6WWZ5U.js.map +0 -1
  314. package/dist/chunk-OEWDTMG7.js.map +0 -1
  315. package/dist/chunk-PWLANIRT.js.map +0 -1
  316. package/dist/chunk-QXHPKYJV.js.map +0 -1
  317. package/dist/chunk-VBXEHIUJ.js.map +0 -1
  318. package/dist/chunk-YKRAFF5K.js.map +0 -1
  319. package/dist/chunk-ZSAAAMVR.js.map +0 -1
  320. package/dist/components.js.map +0 -1
  321. package/dist/contextValidator-OOPCLPZW.js +0 -9
  322. package/dist/contextValidator-OOPCLPZW.js.map +0 -1
  323. package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
  324. package/dist/hooks.js.map +0 -1
  325. package/dist/index.js.map +0 -1
  326. package/dist/providers.js.map +0 -1
  327. package/dist/rbac/eslint-rules.js.map +0 -1
  328. package/dist/rbac/index.js.map +0 -1
  329. package/dist/styles/index.js.map +0 -1
  330. package/dist/theming/runtime.js.map +0 -1
  331. package/dist/types.js.map +0 -1
  332. package/dist/utils.js.map +0 -1
  333. package/docs/standards/01-architecture-standard.md +0 -44
  334. package/docs/standards/02-api-and-rpc-standard.md +0 -39
  335. package/docs/standards/03-component-standard.md +0 -32
  336. package/docs/standards/05-security-standard.md +0 -44
  337. package/docs/standards/06-testing-and-docs-standard.md +0 -29
  338. package/docs/standards/pace-core-compliance.md +0 -432
  339. package/scripts/audit/core/checks/accessibility.cjs +0 -197
  340. package/scripts/audit/core/checks/api-usage.cjs +0 -191
  341. package/scripts/audit/core/checks/bundle.cjs +0 -142
  342. package/scripts/audit/core/checks/compliance.cjs +0 -2706
  343. package/scripts/audit/core/checks/config.cjs +0 -54
  344. package/scripts/audit/core/checks/coverage.cjs +0 -84
  345. package/scripts/audit/core/checks/dependencies.cjs +0 -994
  346. package/scripts/audit/core/checks/documentation.cjs +0 -268
  347. package/scripts/audit/core/checks/environment.cjs +0 -116
  348. package/scripts/audit/core/checks/error-handling.cjs +0 -340
  349. package/scripts/audit/core/checks/forms.cjs +0 -172
  350. package/scripts/audit/core/checks/heuristics.cjs +0 -68
  351. package/scripts/audit/core/checks/hooks.cjs +0 -334
  352. package/scripts/audit/core/checks/imports.cjs +0 -244
  353. package/scripts/audit/core/checks/performance.cjs +0 -325
  354. package/scripts/audit/core/checks/routes.cjs +0 -117
  355. package/scripts/audit/core/checks/state.cjs +0 -130
  356. package/scripts/audit/core/checks/structure.cjs +0 -65
  357. package/scripts/audit/core/checks/style.cjs +0 -584
  358. package/scripts/audit/core/checks/testing.cjs +0 -122
  359. package/scripts/audit/core/checks/typescript.cjs +0 -61
  360. package/scripts/audit/core/scanner.cjs +0 -199
  361. package/scripts/audit/core/utils.cjs +0 -137
  362. package/scripts/audit/reporters/console.cjs +0 -151
  363. package/scripts/audit/reporters/json.cjs +0 -54
  364. package/scripts/audit/reporters/markdown.cjs +0 -124
  365. package/scripts/audit-consuming-app.cjs +0 -86
  366. package/src/eslint-rules/pace-core-compliance.cjs +0 -510
  367. package/src/eslint-rules/pace-core-compliance.js +0 -638
  368. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
  369. package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
  370. package/src/rbac/components/NavigationProvider.test.tsx +0 -481
  371. package/src/rbac/components/NavigationProvider.tsx +0 -345
  372. package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
  373. package/src/rbac/components/PagePermissionProvider.tsx +0 -279
  374. package/src/rbac/components/PermissionEnforcer.tsx +0 -312
  375. package/src/rbac/components/RoleBasedRouter.tsx +0 -440
  376. package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
  377. package/src/rbac/components/SecureDataProvider.tsx +0 -339
  378. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
  379. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
  380. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
  381. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
  382. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
  383. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
  384. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
  385. package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
  386. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
  387. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
@@ -0,0 +1,553 @@
1
+ /**
2
+ * Pattern Detector for RBAC Compliance
3
+ * @package @jmruthers/pace-core
4
+ * @module RBAC/Compliance/PatternDetector
5
+ * @since 1.0.0
6
+ *
7
+ * This module provides static and runtime pattern detection for RBAC violations.
8
+ * It detects direct RPC calls, direct table queries, and other non-standard patterns.
9
+ */
10
+
11
+ import { getRBACLogger } from '../config';
12
+
13
+ export interface PatternViolation {
14
+ type: 'direct-rpc-call' | 'direct-table-query' | 'bypass-pattern' | 'custom-component' | 'hardcoded-role-check' | 'custom-permission-utility' | 'ui-only-access-control' | 'permission-bypass-comment' | 'resource-permission-string-literal' | 'permission-wrapper-function';
15
+ file?: string;
16
+ line?: number;
17
+ message: string;
18
+ recommendation: string;
19
+ }
20
+
21
+ export interface PatternDetectionResult {
22
+ violations: PatternViolation[];
23
+ isCompliant: boolean;
24
+ summary: {
25
+ directRpcCalls: number;
26
+ directTableQueries: number;
27
+ bypassPatterns: number;
28
+ customComponents: number;
29
+ hardcodedRoleChecks: number;
30
+ customPermissionUtilities: number;
31
+ uiOnlyAccessControl: number;
32
+ permissionBypassComments: number;
33
+ resourcePermissionStringLiterals: number;
34
+ permissionWrapperFunctions: number;
35
+ };
36
+ }
37
+
38
+ /**
39
+ * RBAC table names that should not be queried directly
40
+ */
41
+ const RBAC_TABLES = [
42
+ 'rbac_organisation_roles',
43
+ 'rbac_event_app_roles',
44
+ 'rbac_global_roles',
45
+ 'rbac_apps',
46
+ 'rbac_app_pages',
47
+ 'rbac_page_permissions',
48
+ 'rbac_user_profiles'
49
+ ];
50
+
51
+ /**
52
+ * Detect direct RPC calls to rbac_check_permission_simplified
53
+ *
54
+ * @param code - Source code to analyze
55
+ * @param filePath - File path for reporting
56
+ * @returns Array of violations
57
+ */
58
+ export function detectDirectRpcCalls(code: string, filePath?: string): PatternViolation[] {
59
+ const violations: PatternViolation[] = [];
60
+ const lines = code.split('\n');
61
+
62
+ // Skip if this is in pace-core package itself
63
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
64
+ return violations;
65
+ }
66
+
67
+ // Pattern: .rpc('rbac_check_permission_simplified', ...)
68
+ const rpcPattern = /\.rpc\s*\(\s*['"]rbac_check_permission_simplified['"]/g;
69
+ let match;
70
+ let lineNumber = 1;
71
+
72
+ for (const line of lines) {
73
+ if (rpcPattern.test(line)) {
74
+ violations.push({
75
+ type: 'direct-rpc-call',
76
+ file: filePath,
77
+ line: lineNumber,
78
+ message: `Direct RPC call to 'rbac_check_permission_simplified' detected`,
79
+ recommendation: "Use isPermitted() from '@jmruthers/pace-core/rbac' instead of calling the RPC directly."
80
+ });
81
+ }
82
+ lineNumber++;
83
+ }
84
+
85
+ return violations;
86
+ }
87
+
88
+ /**
89
+ * Detect direct queries to RBAC tables
90
+ *
91
+ * @param code - Source code to analyze
92
+ * @param filePath - File path for reporting
93
+ * @returns Array of violations
94
+ */
95
+ export function detectDirectTableQueries(code: string, filePath?: string): PatternViolation[] {
96
+ const violations: PatternViolation[] = [];
97
+ const lines = code.split('\n');
98
+
99
+ // Skip if this is in pace-core package itself
100
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
101
+ return violations;
102
+ }
103
+
104
+ // Pattern: .from('rbac_*')
105
+ const fromPattern = /\.from\s*\(\s*['"](rbac_\w+)['"]/g;
106
+ let lineNumber = 1;
107
+
108
+ for (const line of lines) {
109
+ let match;
110
+ while ((match = fromPattern.exec(line)) !== null) {
111
+ const tableName = match[1];
112
+ if (RBAC_TABLES.includes(tableName) || tableName.startsWith('rbac_')) {
113
+ violations.push({
114
+ type: 'direct-table-query',
115
+ file: filePath,
116
+ line: lineNumber,
117
+ message: `Direct query to RBAC table '${tableName}' detected`,
118
+ recommendation: `Use pace-core RBAC API functions from '@jmruthers/pace-core/rbac' instead of querying '${tableName}' directly.`
119
+ });
120
+ }
121
+ }
122
+ lineNumber++;
123
+ }
124
+
125
+ return violations;
126
+ }
127
+
128
+ /**
129
+ * Detect custom access denied components
130
+ *
131
+ * @param code - Source code to analyze
132
+ * @param filePath - File path for reporting
133
+ * @returns Array of violations
134
+ */
135
+ export function detectCustomAccessDeniedComponents(code: string, filePath?: string): PatternViolation[] {
136
+ const violations: PatternViolation[] = [];
137
+ const lines = code.split('\n');
138
+
139
+ // Skip if this is in pace-core package itself
140
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
141
+ return violations;
142
+ }
143
+
144
+ // Check if AccessDenied is imported from pace-core
145
+ const hasPaceCoreAccessDenied = /from\s+['"]@jmruthers\/pace-core\/rbac['"].*AccessDenied/.test(code) ||
146
+ /import.*AccessDenied.*from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(code);
147
+
148
+ if (hasPaceCoreAccessDenied) {
149
+ return violations; // Using pace-core AccessDenied, no violation
150
+ }
151
+
152
+ // Pattern: function AccessDenied, const AccessDenied, export AccessDenied, etc.
153
+ const accessDeniedPatterns = [
154
+ /(?:function|const|export\s+(?:function|const)?)\s+(AccessDenied|PermissionDenied|AccessDeniedPage|PermissionDeniedPage|Unauthorized|UnauthorizedPage)\s*[=:]/g,
155
+ /export\s+default\s+(?:function\s+)?(AccessDenied|PermissionDenied|AccessDeniedPage|PermissionDeniedPage|Unauthorized|UnauthorizedPage)/g
156
+ ];
157
+
158
+ let lineNumber = 1;
159
+
160
+ for (const line of lines) {
161
+ for (const pattern of accessDeniedPatterns) {
162
+ const match = pattern.exec(line);
163
+ if (match) {
164
+ const componentName = match[1];
165
+ violations.push({
166
+ type: 'custom-component',
167
+ file: filePath,
168
+ line: lineNumber,
169
+ message: `Custom access denied component '${componentName}' detected`,
170
+ recommendation: `Use AccessDenied from '@jmruthers/pace-core/rbac' instead of creating a custom '${componentName}' component.`
171
+ });
172
+ }
173
+ }
174
+ lineNumber++;
175
+ }
176
+
177
+ return violations;
178
+ }
179
+
180
+ /**
181
+ * Detect hardcoded role checks (e.g., role === 'admin', user.role === 'org_admin')
182
+ *
183
+ * @param code - Source code to analyze
184
+ * @param filePath - File path for reporting
185
+ * @returns Array of violations
186
+ */
187
+ export function detectHardcodedRoleChecks(code: string, filePath?: string): PatternViolation[] {
188
+ const violations: PatternViolation[] = [];
189
+ const lines = code.split('\n');
190
+
191
+ // Skip if this is in pace-core package itself
192
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
193
+ return violations;
194
+ }
195
+
196
+ // Pattern: role === 'admin', user.role === 'org_admin', etc.
197
+ // Common role names to detect
198
+ const roleNames = ['admin', 'org_admin', 'event_admin', 'user', 'member', 'viewer', 'editor'];
199
+ const roleCheckPattern = new RegExp(
200
+ `(?:role|user\\.role|userRole|currentRole|userRoleName)\\s*(?:===|!==|==|!=)\\s*['"](${roleNames.join('|')})['"]`,
201
+ 'gi'
202
+ );
203
+
204
+ let lineNumber = 1;
205
+
206
+ for (const line of lines) {
207
+ const match = roleCheckPattern.exec(line);
208
+ if (match) {
209
+ const roleName = match[1];
210
+ violations.push({
211
+ type: 'hardcoded-role-check',
212
+ file: filePath,
213
+ line: lineNumber,
214
+ message: `Hardcoded role check detected: comparing role to '${roleName}'`,
215
+ recommendation: "Use useAccessLevel hook or getRoleContext API from '@jmruthers/pace-core/rbac' instead of hardcoded role checks."
216
+ });
217
+ }
218
+ lineNumber++;
219
+ }
220
+
221
+ return violations;
222
+ }
223
+
224
+ /**
225
+ * Detect custom permission utility functions (e.g., checkPermission, hasPermission, canAccess)
226
+ *
227
+ * @param code - Source code to analyze
228
+ * @param filePath - File path for reporting
229
+ * @returns Array of violations
230
+ */
231
+ export function detectCustomPermissionUtilities(code: string, filePath?: string): PatternViolation[] {
232
+ const violations: PatternViolation[] = [];
233
+ const lines = code.split('\n');
234
+
235
+ // Skip if this is in pace-core package itself
236
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
237
+ return violations;
238
+ }
239
+
240
+ // Check if pace-core permission functions are imported
241
+ const hasPaceCoreImports = /from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(code);
242
+
243
+ // Pattern: function checkPermission, const hasPermission, export canAccess, etc.
244
+ const permissionUtilityPatterns = [
245
+ /(?:function|const|export\s+(?:function|const)?)\s+(checkPermission|hasPermission|canAccess|checkAccess|verifyPermission|hasAccess|canPerform|checkCan|hasCan)\s*[=:]/g,
246
+ /export\s+default\s+(?:function\s+)?(checkPermission|hasPermission|canAccess|checkAccess|verifyPermission|hasAccess|canPerform|checkCan|hasCan)/g
247
+ ];
248
+
249
+ let lineNumber = 1;
250
+
251
+ for (const line of lines) {
252
+ for (const pattern of permissionUtilityPatterns) {
253
+ const match = pattern.exec(line);
254
+ if (match) {
255
+ const functionName = match[1];
256
+ // Only flag if not importing from pace-core (might be using pace-core functions)
257
+ if (!hasPaceCoreImports || !line.includes('@jmruthers/pace-core')) {
258
+ violations.push({
259
+ type: 'custom-permission-utility',
260
+ file: filePath,
261
+ line: lineNumber,
262
+ message: `Custom permission utility function '${functionName}' detected`,
263
+ recommendation: `Use isPermitted API or useCan hook from '@jmruthers/pace-core/rbac' instead of creating custom permission utilities.`
264
+ });
265
+ }
266
+ }
267
+ }
268
+ lineNumber++;
269
+ }
270
+
271
+ return violations;
272
+ }
273
+
274
+ /**
275
+ * Detect permission bypass comments (e.g., TODO: add permission check, FIXME: security)
276
+ *
277
+ * @param code - Source code to analyze
278
+ * @param filePath - File path for reporting
279
+ * @returns Array of violations
280
+ */
281
+ export function detectPermissionBypassComments(code: string, filePath?: string): PatternViolation[] {
282
+ const violations: PatternViolation[] = [];
283
+ const lines = code.split('\n');
284
+
285
+ // Skip if this is in pace-core package itself
286
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
287
+ return violations;
288
+ }
289
+
290
+ // Pattern: TODO/FIXME comments about permissions or security
291
+ const bypassCommentPattern = /(?:TODO|FIXME|XXX|HACK|NOTE):\s*(?:add|missing|need|fix|implement|check)\s+(?:permission|rbac|access|security|authorization|auth)/gi;
292
+
293
+ let lineNumber = 1;
294
+
295
+ for (const line of lines) {
296
+ if (bypassCommentPattern.test(line)) {
297
+ violations.push({
298
+ type: 'permission-bypass-comment',
299
+ file: filePath,
300
+ line: lineNumber,
301
+ message: `Permission bypass comment detected: ${line.trim()}`,
302
+ recommendation: "Add proper permission checks using pace-core APIs (isPermitted, useCan, useResourcePermissions) from '@jmruthers/pace-core/rbac'."
303
+ });
304
+ }
305
+ lineNumber++;
306
+ }
307
+
308
+ return violations;
309
+ }
310
+
311
+ /**
312
+ * Detect permission wrapper functions that wrap pace-core hooks with additional logic
313
+ *
314
+ * @param code - Source code to analyze
315
+ * @param filePath - File path for reporting
316
+ * @returns Array of violations
317
+ */
318
+ export function detectPermissionWrapperFunctions(code: string, filePath?: string): PatternViolation[] {
319
+ const violations: PatternViolation[] = [];
320
+ const lines = code.split('\n');
321
+
322
+ // Skip if this is in pace-core package itself
323
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
324
+ return violations;
325
+ }
326
+
327
+ // Check if pace-core RBAC is imported
328
+ const hasPaceCoreRBACImport = /from\s+['"]@jmruthers\/pace-core\/rbac['"]/.test(code);
329
+
330
+ if (!hasPaceCoreRBACImport) {
331
+ return violations; // Can't have wrapper functions without pace-core imports
332
+ }
333
+
334
+ // Pattern: Functions that start with 'can' and use permission hooks/functions with additional logic
335
+ // Examples:
336
+ // - const canEdit = (postId: string) => { const hasPermission = canUpdate('journal'); const post = posts.find(...); return hasPermission && !!post; };
337
+ // - function canDeletePost(postId: string) { if (!canDelete('journal')) return false; return posts.find(...); }
338
+ const permissionHookNames = ['useCan', 'useResourcePermissions', 'usePermissions', 'useMultiplePermissions'];
339
+ const permissionFunctionNames = ['canCreate', 'canUpdate', 'canDelete', 'canRead', 'can'];
340
+
341
+ // Simple pattern matching for common wrapper function patterns
342
+ // This is a heuristic - it looks for functions that:
343
+ // 1. Start with 'can' or contain 'permission'/'access'
344
+ // 2. Use permission hooks or functions
345
+ // 3. Have additional logic (&&, ||, .find, .filter, etc.)
346
+
347
+ let lineNumber = 1;
348
+ let functionStart = -1;
349
+ let functionName = '';
350
+ let functionBody = '';
351
+ let braceDepth = 0;
352
+ let inFunction = false;
353
+
354
+ for (const line of lines) {
355
+ // Check for function start
356
+ const functionMatch = line.match(/(?:function|const|export\s+(?:function|const)?)\s+((?:can\w+|.*Permission.*|.*Access.*))\s*[=:]/);
357
+ if (functionMatch && !inFunction) {
358
+ const name = functionMatch[1];
359
+ // Check if name suggests a permission wrapper
360
+ if (name.toLowerCase().startsWith('can') ||
361
+ name.toLowerCase().includes('permission') ||
362
+ name.toLowerCase().includes('access')) {
363
+ inFunction = true;
364
+ functionStart = lineNumber;
365
+ functionName = name;
366
+ functionBody = line;
367
+ braceDepth = (line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
368
+ continue;
369
+ }
370
+ }
371
+
372
+ // If we're in a function, accumulate the body
373
+ if (inFunction) {
374
+ functionBody += '\n' + line;
375
+ braceDepth += (line.match(/{/g) || []).length - (line.match(/}/g) || []).length;
376
+
377
+ // Check if function uses permission hooks/functions
378
+ const usesPermissionHooks = permissionHookNames.some(hook => functionBody.includes(hook));
379
+ const usesPermissionFunctions = permissionFunctionNames.some(fn => functionBody.includes(fn));
380
+
381
+ // Check if function has additional logic beyond permission checking
382
+ const hasAdditionalLogic = (
383
+ functionBody.includes('&&') ||
384
+ functionBody.includes('||') ||
385
+ functionBody.includes('if') ||
386
+ functionBody.includes('.find') ||
387
+ functionBody.includes('.filter') ||
388
+ functionBody.includes('.map') ||
389
+ (functionBody.match(/return/g) || []).length > 1
390
+ );
391
+
392
+ // Function ends when brace depth returns to 0 or we hit a semicolon for arrow functions
393
+ if (braceDepth <= 0 || (functionBody.includes('=>') && line.includes(';') && braceDepth === 0)) {
394
+ // Check if this is a wrapper function
395
+ if ((usesPermissionHooks || usesPermissionFunctions) && hasAdditionalLogic && functionBody.includes('(') && functionBody.includes(')')) {
396
+ violations.push({
397
+ type: 'permission-wrapper-function',
398
+ file: filePath,
399
+ line: functionStart,
400
+ message: `Permission wrapper function '${functionName}' detected`,
401
+ recommendation: "Use pace-core hooks (useCan, useResourcePermissions) directly in components instead of wrapping them with additional logic."
402
+ });
403
+ }
404
+
405
+ // Reset
406
+ inFunction = false;
407
+ functionBody = '';
408
+ functionName = '';
409
+ braceDepth = 0;
410
+ functionStart = -1;
411
+ }
412
+ }
413
+
414
+ lineNumber++;
415
+ }
416
+
417
+ return violations;
418
+ }
419
+
420
+ /**
421
+ * Detect resource permission string literals (e.g., useResourcePermissions('journal'))
422
+ *
423
+ * @param code - Source code to analyze
424
+ * @param filePath - File path for reporting
425
+ * @returns Array of violations
426
+ */
427
+ export function detectResourcePermissionStringLiterals(code: string, filePath?: string): PatternViolation[] {
428
+ const violations: PatternViolation[] = [];
429
+ const lines = code.split('\n');
430
+
431
+ // Skip if this is in pace-core package itself
432
+ if (filePath && (filePath.includes('packages/core/src/rbac') || filePath.includes('packages\\core\\src\\rbac'))) {
433
+ return violations;
434
+ }
435
+
436
+ // Check if RESOURCE_NAMES constant is imported
437
+ const hasResourceNamesImport = /import.*RESOURCE_NAMES.*from/.test(code);
438
+
439
+ // Pattern: useResourcePermissions('string-literal') or useResourcePermissions("string-literal")
440
+ // Exclude if using RESOURCE_NAMES constant
441
+ const resourcePermissionPattern = /useResourcePermissions\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
442
+
443
+ let lineNumber = 1;
444
+
445
+ for (const line of lines) {
446
+ // Skip if line uses RESOURCE_NAMES constant
447
+ if (line.includes('RESOURCE_NAMES.')) {
448
+ lineNumber++;
449
+ continue;
450
+ }
451
+
452
+ let match;
453
+ while ((match = resourcePermissionPattern.exec(line)) !== null) {
454
+ const resourceName = match[1];
455
+ // Only flag if not using RESOURCE_NAMES constant
456
+ if (!hasResourceNamesImport || !line.includes('RESOURCE_NAMES')) {
457
+ violations.push({
458
+ type: 'resource-permission-string-literal',
459
+ file: filePath,
460
+ line: lineNumber,
461
+ message: `Resource permission string literal detected: useResourcePermissions('${resourceName}')`,
462
+ recommendation: "Use RESOURCE_NAMES constant object instead of string literals. Create RESOURCE_NAMES constant and use RESOURCE_NAMES.JOURNAL, RESOURCE_NAMES.RISKS, etc."
463
+ });
464
+ }
465
+ }
466
+ lineNumber++;
467
+ }
468
+
469
+ return violations;
470
+ }
471
+
472
+ /**
473
+ * Detect all RBAC pattern violations in code
474
+ *
475
+ * @param code - Source code to analyze
476
+ * @param filePath - File path for reporting
477
+ * @returns Pattern detection result
478
+ */
479
+ export function detectPatternViolations(code: string, filePath?: string): PatternDetectionResult {
480
+ const rpcViolations = detectDirectRpcCalls(code, filePath);
481
+ const tableViolations = detectDirectTableQueries(code, filePath);
482
+ const componentViolations = detectCustomAccessDeniedComponents(code, filePath);
483
+ const roleCheckViolations = detectHardcodedRoleChecks(code, filePath);
484
+ const permissionUtilityViolations = detectCustomPermissionUtilities(code, filePath);
485
+ const bypassCommentViolations = detectPermissionBypassComments(code, filePath);
486
+ const resourceLiteralViolations = detectResourcePermissionStringLiterals(code, filePath);
487
+ const wrapperFunctionViolations = detectPermissionWrapperFunctions(code, filePath);
488
+
489
+ const allViolations = [
490
+ ...rpcViolations,
491
+ ...tableViolations,
492
+ ...componentViolations,
493
+ ...roleCheckViolations,
494
+ ...permissionUtilityViolations,
495
+ ...bypassCommentViolations,
496
+ ...resourceLiteralViolations,
497
+ ...wrapperFunctionViolations
498
+ ];
499
+
500
+ return {
501
+ violations: allViolations,
502
+ isCompliant: allViolations.length === 0,
503
+ summary: {
504
+ directRpcCalls: rpcViolations.length,
505
+ directTableQueries: tableViolations.length,
506
+ bypassPatterns: 0, // This would require route analysis
507
+ customComponents: componentViolations.length,
508
+ hardcodedRoleChecks: roleCheckViolations.length,
509
+ customPermissionUtilities: permissionUtilityViolations.length,
510
+ uiOnlyAccessControl: 0, // This would require cross-file analysis
511
+ permissionBypassComments: bypassCommentViolations.length,
512
+ resourcePermissionStringLiterals: resourceLiteralViolations.length,
513
+ permissionWrapperFunctions: wrapperFunctionViolations.length
514
+ }
515
+ };
516
+ }
517
+
518
+ /**
519
+ * Log pattern violations to console
520
+ *
521
+ * @param result - Pattern detection result
522
+ */
523
+ export function logPatternViolations(result: PatternDetectionResult): void {
524
+ const logger = getRBACLogger();
525
+
526
+ if (result.isCompliant) {
527
+ logger.info('[RBAC Pattern Detection] No violations detected');
528
+ return;
529
+ }
530
+
531
+ logger.warn(`[RBAC Pattern Detection] Found ${result.violations.length} violation(s):`);
532
+
533
+ result.violations.forEach((violation, index) => {
534
+ const location = violation.file && violation.line
535
+ ? `${violation.file}:${violation.line}`
536
+ : violation.file || 'unknown location';
537
+
538
+ logger.warn(
539
+ ` ${index + 1}. [${violation.type}] ${violation.message}\n` +
540
+ ` Location: ${location}\n` +
541
+ ` Recommendation: ${violation.recommendation}`
542
+ );
543
+ });
544
+
545
+ logger.warn(`\nSummary: ${result.summary.directRpcCalls} direct RPC calls, ` +
546
+ `${result.summary.directTableQueries} direct table queries, ` +
547
+ `${result.summary.customComponents} custom components, ` +
548
+ `${result.summary.hardcodedRoleChecks} hardcoded role checks, ` +
549
+ `${result.summary.customPermissionUtilities} custom permission utilities, ` +
550
+ `${result.summary.permissionBypassComments} permission bypass comments, ` +
551
+ `${result.summary.resourcePermissionStringLiterals} resource permission string literals`);
552
+ }
553
+
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { validateRBACSetup, SetupIssue } from './setup-validator';
11
11
  import { getRBACLogger } from '../config';
12
+ import { detectPatternViolations, logPatternViolations, type PatternDetectionResult } from './pattern-detector';
12
13
 
13
14
  export interface RuntimeComplianceResult {
14
15
  setup: {
@@ -20,6 +21,7 @@ export interface RuntimeComplianceResult {
20
21
  available: boolean;
21
22
  message?: string;
22
23
  };
24
+ patternDetection?: PatternDetectionResult;
23
25
  }
24
26
 
25
27
  /**
@@ -75,3 +77,23 @@ export function validateAndWarn(): void {
75
77
  }
76
78
  }
77
79
 
80
+ /**
81
+ * Check pattern violations in source code
82
+ *
83
+ * This function analyzes source code for RBAC pattern violations.
84
+ * It can be used for static analysis or runtime code inspection.
85
+ *
86
+ * @param code - Source code to analyze
87
+ * @param filePath - Optional file path for better error reporting
88
+ * @returns Pattern detection result
89
+ */
90
+ export function checkPatternCompliance(code: string, filePath?: string): PatternDetectionResult {
91
+ const result = detectPatternViolations(code, filePath);
92
+
93
+ if (!result.isCompliant) {
94
+ logPatternViolations(result);
95
+ }
96
+
97
+ return result;
98
+ }
99
+