@jmruthers/pace-core 0.6.5 → 0.6.7
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/CHANGELOG.md +104 -0
- package/README.md +5 -403
- package/audit-tool/00-dependencies.cjs +394 -0
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +255 -0
- package/audit-tool/audits/03-architecture.cjs +196 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +544 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +301 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +291 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +241 -0
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/01-pace-core-compliance.mdc +586 -0
- package/cursor-rules/02-project-structure.mdc +42 -4
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +126 -10
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +104 -34
- package/cursor-rules/06-security-rbac.mdc +518 -0
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/DataTable-7PMH7XN7.js +15 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-QTFVrL-Z.d.ts → PublicPageProvider-DlsCaR5v.d.ts} +33 -72
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/{chunk-UPPMRMYG.js → chunk-5X4QLXRG.js} +73 -151
- package/dist/chunk-6F3IILHI.js +62 -0
- package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
- package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
- package/dist/{chunk-FMUCXFII.js → chunk-7ILTDCL2.js} +9 -5
- package/dist/{chunk-M43Y4SSO.js → chunk-A3W6LW53.js} +15 -13
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-HU2C6SSC.js → chunk-BM4CQ5P3.js} +606 -559
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-J36DSWQK.js → chunk-FEJLJNWA.js} +7 -41
- package/dist/{chunk-IHB5DR3H.js → chunk-FTCRZOG2.js} +188 -387
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/chunk-GHYHJTYV.js +994 -0
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-FFQEQTNW.js → chunk-IUBRCBSY.js} +134 -45
- package/dist/{chunk-6COVEUS7.js → chunk-JGWDVX64.js} +983 -1034
- package/dist/{chunk-RGAWHO7N.js → chunk-L4XMVJKY.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/{chunk-M7MPQISP.js → chunk-OJ4SKRSV.js} +3 -16
- package/dist/{chunk-IVOFDYWT.js → chunk-Q7Q7V5NV.js} +2109 -1604
- package/dist/{chunk-JGRYX5UX.js → chunk-S7DKJPLT.js} +29 -58
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-NTM7ZSB6.js → chunk-VBCS3DUA.js} +261 -168
- package/dist/{chunk-EFN2EIMK.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-L4OXEN46.js → chunk-ZKAWKYT4.js} +10 -24
- package/dist/components.d.ts +7 -5
- package/dist/components.js +46 -257
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +35 -0
- package/{src/eslint-rules/pace-core-compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +234 -235
- package/dist/eslint-rules/rules/04-code-quality.cjs +290 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +806 -0
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +263 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/eslint-rules/utils/helpers.cjs +42 -0
- package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +62 -172
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +12 -11
- package/dist/index.js +67 -660
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +8 -35
- package/dist/rbac/eslint-rules.d.ts +46 -44
- package/dist/rbac/eslint-rules.js +7 -4
- package/dist/rbac/index.d.ts +109 -586
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- package/dist/theming/runtime.d.ts +14 -1
- package/dist/theming/runtime.js +3 -19
- package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
- package/dist/{types-CkbwOr4Y.d.ts → types-DXstZpNI.d.ts} +4 -17
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-ClnV4tnv.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +20 -20
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +17 -7
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +301 -871
- package/docs/api-reference/components.md +21 -21
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +126 -33
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +5 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +17 -8
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -35
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/1-pace-core-compliance-standards.md +986 -0
- package/docs/standards/2-project-structure-standards.md +949 -0
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/5-styling-standards.md +348 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 6-security-rbac-standards.md} +269 -66
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +185 -57
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +33 -6
- package/package.json +35 -23
- package/scripts/install-cursor-rules.cjs +25 -6
- package/scripts/install-eslint-config.cjs +284 -0
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +3 -3
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +13 -13
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +9 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/ContextSelector/ContextSelector.tsx +106 -119
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +10 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +10 -4
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +35 -21
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +12 -9
- package/src/components/DataTable/components/EmptyState.tsx +10 -9
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +41 -27
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
- package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
- package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
- package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
- package/src/components/DataTable/types.ts +5 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +2 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +10 -9
- package/src/components/Dialog/Dialog.test.tsx +128 -104
- package/src/components/Dialog/Dialog.tsx +742 -24
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileDisplay/FileDisplay.test.tsx +4 -2
- package/src/components/FileDisplay/FileDisplay.tsx +23 -17
- package/src/components/FileUpload/FileUpload.test.tsx +52 -14
- package/src/components/FileUpload/FileUpload.tsx +112 -130
- package/src/components/Form/Form.test.tsx +6 -8
- package/src/components/Form/Form.tsx +365 -4
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +11 -15
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +109 -98
- package/src/components/Select/types.ts +4 -1
- package/src/components/UserMenu/UserMenu.tsx +9 -6
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +55 -57
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +97 -97
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
- package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
- package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
- package/src/hooks/public/usePublicEvent.ts +67 -195
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +24 -14
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +5 -5
- package/src/hooks/useAppConfig.ts +28 -26
- package/src/hooks/useEventTheme.test.ts +217 -239
- package/src/hooks/useEventTheme.ts +16 -28
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useOrganisationPermissions.ts +5 -7
- package/src/hooks/useQueryCache.ts +0 -1
- package/src/hooks/useSessionDraft.ts +380 -0
- package/src/hooks/useSessionRestoration.ts +3 -1
- package/src/icons/index.ts +27 -0
- package/src/index.ts +5 -0
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +37 -37
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +13 -10
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
- package/src/rbac/adapters.tsx +7 -295
- package/src/rbac/api.test.ts +44 -56
- package/src/rbac/api.ts +10 -17
- package/src/rbac/cache-invalidation.ts +0 -1
- package/src/rbac/compliance/index.ts +10 -0
- package/src/rbac/compliance/pattern-detector.ts +553 -0
- package/src/rbac/compliance/runtime-compliance.ts +22 -0
- package/src/rbac/components/AccessDenied.tsx +150 -0
- package/src/rbac/components/NavigationGuard.tsx +12 -20
- package/src/rbac/components/PagePermissionGuard.tsx +4 -24
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
- package/src/rbac/components/index.ts +3 -41
- package/src/rbac/eslint-rules.js +1 -1
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/permissions/index.ts +0 -3
- package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
- package/src/rbac/hooks/usePermissions.ts +0 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
- package/src/rbac/hooks/useResolvedScope.ts +58 -140
- package/src/rbac/hooks/useResourcePermissions.test.ts +124 -38
- package/src/rbac/hooks/useResourcePermissions.ts +139 -48
- package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
- package/src/rbac/hooks/useRoleManagement.ts +147 -19
- package/src/rbac/hooks/useSecureSupabase.ts +4 -8
- package/src/rbac/index.ts +7 -9
- package/src/rbac/utils/contextValidator.ts +9 -7
- package/src/services/AuthService.ts +130 -18
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +16 -0
- package/src/services/OrganisationService.ts +7 -44
- package/src/services/__tests__/OrganisationService.test.ts +26 -8
- package/src/services/base/BaseService.ts +0 -3
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +9 -3
- package/src/theming/parseEventColours.ts +22 -10
- package/src/types/database.generated.ts +4733 -3809
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
- package/src/utils/context/organisationContext.test.ts +13 -28
- package/src/utils/context/organisationContext.ts +21 -52
- package/src/utils/dynamic/dynamicUtils.ts +1 -1
- package/src/utils/file-reference/index.ts +39 -15
- package/src/utils/formatting/formatDateTime.test.ts +3 -2
- package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
- package/src/utils/index.ts +4 -1
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
- package/src/utils/persistence/keyDerivation.ts +304 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
- package/src/utils/security/secureStorage.ts +5 -5
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/helpers.ts +3 -3
- package/src/utils/supabase/createBaseClient.ts +147 -0
- package/src/utils/timezone/timezone.test.ts +1 -2
- package/src/utils/timezone/timezone.ts +1 -1
- package/src/utils/validation/csrf.ts +4 -4
- package/cursor-rules/00-pace-core-compliance.mdc +0 -331
- package/cursor-rules/01-standards-compliance.mdc +0 -244
- package/cursor-rules/04-testing-standards.mdc +0 -268
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -246
- package/cursor-rules/06-code-quality.mdc +0 -309
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -214
- package/cursor-rules/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-AOVNCPTX.js +0 -175
- package/dist/DataTable-AOVNCPTX.js.map +0 -1
- package/dist/UnifiedAuthProvider-4SBX4LU5.js +0 -18
- package/dist/UnifiedAuthProvider-4SBX4LU5.js.map +0 -1
- package/dist/api-O6HTBX5Y.js +0 -52
- package/dist/api-O6HTBX5Y.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6COVEUS7.js.map +0 -1
- package/dist/chunk-AFVQODI2.js +0 -263
- package/dist/chunk-AFVQODI2.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.js.map +0 -1
- package/dist/chunk-EFN2EIMK.js.map +0 -1
- package/dist/chunk-FFQEQTNW.js.map +0 -1
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-G37KK66H.js.map +0 -1
- package/dist/chunk-G7QEZTYQ.js +0 -2053
- package/dist/chunk-G7QEZTYQ.js.map +0 -1
- package/dist/chunk-HU2C6SSC.js.map +0 -1
- package/dist/chunk-IHB5DR3H.js.map +0 -1
- package/dist/chunk-IVOFDYWT.js.map +0 -1
- package/dist/chunk-J36DSWQK.js.map +0 -1
- package/dist/chunk-JGRYX5UX.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js +0 -1
- package/dist/chunk-KQCRWDSA.js.map +0 -1
- package/dist/chunk-L4OXEN46.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-M43Y4SSO.js.map +0 -1
- package/dist/chunk-M7MPQISP.js.map +0 -1
- package/dist/chunk-NTM7ZSB6.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-RGAWHO7N.js.map +0 -1
- package/dist/chunk-UPPMRMYG.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-5OGXSPKS.js +0 -9
- package/dist/contextValidator-5OGXSPKS.js.map +0 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
- package/dist/hooks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers.js.map +0 -1
- package/dist/rbac/eslint-rules.js.map +0 -1
- package/dist/rbac/index.js.map +0 -1
- package/dist/styles/index.js.map +0 -1
- package/dist/theming/runtime.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -601
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-architecture-standard.md +0 -44
- package/docs/standards/02-api-and-rpc-standard.md +0 -39
- package/docs/standards/03-component-standard.md +0 -32
- package/docs/standards/04-code-style-standard.md +0 -32
- package/docs/standards/05-security-standard.md +0 -44
- package/docs/standards/06-testing-and-docs-standard.md +0 -29
- package/docs/standards/pace-core-compliance.md +0 -432
- package/scripts/audit/core/checks/accessibility.cjs +0 -197
- package/scripts/audit/core/checks/api-usage.cjs +0 -191
- package/scripts/audit/core/checks/bundle.cjs +0 -142
- package/scripts/audit/core/checks/compliance.cjs +0 -2706
- package/scripts/audit/core/checks/config.cjs +0 -54
- package/scripts/audit/core/checks/coverage.cjs +0 -84
- package/scripts/audit/core/checks/dependencies.cjs +0 -994
- package/scripts/audit/core/checks/documentation.cjs +0 -268
- package/scripts/audit/core/checks/environment.cjs +0 -116
- package/scripts/audit/core/checks/error-handling.cjs +0 -340
- package/scripts/audit/core/checks/forms.cjs +0 -172
- package/scripts/audit/core/checks/heuristics.cjs +0 -68
- package/scripts/audit/core/checks/hooks.cjs +0 -334
- package/scripts/audit/core/checks/imports.cjs +0 -244
- package/scripts/audit/core/checks/performance.cjs +0 -325
- package/scripts/audit/core/checks/routes.cjs +0 -117
- package/scripts/audit/core/checks/state.cjs +0 -130
- package/scripts/audit/core/checks/structure.cjs +0 -65
- package/scripts/audit/core/checks/style.cjs +0 -584
- package/scripts/audit/core/checks/testing.cjs +0 -122
- package/scripts/audit/core/checks/typescript.cjs +0 -61
- package/scripts/audit/core/scanner.cjs +0 -199
- package/scripts/audit/core/utils.cjs +0 -137
- package/scripts/audit/index.cjs +0 -223
- package/scripts/audit/reporters/console.cjs +0 -151
- package/scripts/audit/reporters/json.cjs +0 -54
- package/scripts/audit/reporters/markdown.cjs +0 -124
- package/scripts/audit-consuming-app.cjs +0 -86
- package/src/components/DataTable/components/DataTableBody.tsx +0 -454
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/pace-core-compliance.js +0 -638
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
- package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
- package/src/rbac/components/NavigationProvider.test.tsx +0 -481
- package/src/rbac/components/NavigationProvider.tsx +0 -345
- package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
- package/src/rbac/components/PagePermissionProvider.tsx +0 -279
- package/src/rbac/components/PermissionEnforcer.tsx +0 -312
- package/src/rbac/components/RoleBasedRouter.tsx +0 -440
- package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
- package/src/rbac/components/SecureDataProvider.tsx +0 -339
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
- 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
|
+
|