@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.
- package/CHANGELOG.md +104 -0
- package/README.md +5 -403
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-pace-core-compliance.mdc +128 -26
- package/cursor-rules/01-standards-compliance.mdc +49 -8
- package/cursor-rules/02-project-structure.mdc +6 -0
- package/cursor-rules/03-solid-principles.mdc +2 -0
- package/cursor-rules/04-testing-standards.mdc +2 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
- package/cursor-rules/06-code-quality.mdc +2 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
- package/cursor-rules/08-markup-quality.mdc +52 -27
- package/cursor-rules/09-rbac-compliance.mdc +462 -0
- package/cursor-rules/10-error-handling-patterns.mdc +179 -0
- package/cursor-rules/11-performance-optimization.mdc +169 -0
- package/cursor-rules/12-ci-cd-integration.mdc +150 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
- package/dist/DataTable-LRJL4IRV.js +15 -0
- package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
- package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
- package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/chunk-4T7OBVTU.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-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
- package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
- package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
- package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/chunk-OHIK3MIO.js +994 -0
- package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
- package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
- package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
- package/dist/components.d.ts +6 -6
- package/dist/components.js +57 -267
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +22 -0
- package/dist/eslint-rules/rules/compliance.cjs +348 -0
- package/dist/eslint-rules/rules/components.cjs +113 -0
- package/dist/eslint-rules/rules/imports.cjs +102 -0
- package/dist/eslint-rules/rules/rbac.cjs +790 -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 +5 -5
- package/dist/hooks.js +62 -270
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +36 -26
- package/dist/index.js +87 -690
- 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 +124 -594
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- 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-B-K_5VnO.d.ts} +4 -0
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +16 -6
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +454 -930
- package/docs/api-reference/components.md +3 -1
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/best-practices/accessibility.md +6 -3
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/permission-enforcement.md +4 -0
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +12 -3
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/secure-client-protection.md +0 -34
- package/docs/standards/00-pace-core-compliance.md +967 -0
- package/docs/standards/01-standards-compliance.md +188 -0
- package/docs/standards/02-project-structure.md +985 -0
- package/docs/standards/03-solid-principles.md +39 -0
- package/docs/standards/04-testing-standards.md +36 -0
- package/docs/standards/05-bug-reports-and-features.md +27 -0
- package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
- package/docs/standards/07-tech-stack-compliance.md +30 -0
- package/docs/standards/08-markup-quality.md +345 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
- package/docs/standards/10-error-handling-patterns.md +401 -0
- package/docs/standards/11-performance-optimization.md +348 -0
- package/docs/standards/12-ci-cd-integration.md +370 -0
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
- package/docs/standards/README.md +62 -33
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +20 -4
- package/package.json +31 -21
- package/scripts/audit/audit-compliance.cjs +1295 -0
- package/scripts/audit/audit-components.cjs +260 -0
- package/scripts/audit/audit-dependencies.cjs +395 -0
- package/scripts/audit/audit-rbac.cjs +954 -0
- package/scripts/audit/audit-standards.cjs +1268 -0
- package/scripts/audit/index.cjs +1898 -194
- package/scripts/install-cursor-rules.cjs +259 -8
- package/scripts/validate-master.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
- 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-utils.test.tsx +3 -3
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/ContextSelector/ContextSelector.tsx +42 -39
- package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
- package/src/components/DataTable/components/DataTableBody.tsx +55 -31
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +7 -2
- package/src/components/DataTable/components/ImportModal.tsx +4 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/core/DataTableContext.tsx +1 -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 -0
- package/src/components/DateTimeField/DateTimeField.tsx +20 -20
- package/src/components/DateTimeField/README.md +5 -2
- package/src/components/Dialog/Dialog.test.tsx +361 -318
- package/src/components/Dialog/Dialog.tsx +1154 -323
- package/src/components/Dialog/index.ts +3 -3
- package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
- package/src/components/FileDisplay/FileDisplay.tsx +28 -22
- package/src/components/Form/Form.test.tsx +9 -10
- package/src/components/Form/Form.tsx +369 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
- package/src/components/LoginForm/LoginForm.tsx +2 -2
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
- package/src/components/PaceAppLayout/README.md +10 -9
- package/src/components/PaceAppLayout/test-setup.tsx +40 -31
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
- package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
- package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
- package/src/components/Select/Select.tsx +23 -21
- package/src/components/Select/types.ts +1 -1
- package/src/components/UserMenu/UserMenu.test.tsx +38 -6
- package/src/components/UserMenu/UserMenu.tsx +39 -34
- package/src/components/index.ts +3 -4
- package/src/eslint-rules/index.cjs +22 -0
- package/src/eslint-rules/rules/compliance.cjs +348 -0
- package/src/eslint-rules/rules/components.cjs +113 -0
- package/src/eslint-rules/rules/imports.cjs +102 -0
- package/src/eslint-rules/rules/rbac.cjs +790 -0
- package/src/eslint-rules/utils/helpers.cjs +42 -0
- package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- 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 +62 -190
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +19 -9
- package/src/hooks/useAppConfig.ts +26 -24
- package/src/hooks/useEventTheme.test.ts +211 -233
- package/src/hooks/useEventTheme.ts +19 -28
- package/src/hooks/useEvents.ts +11 -7
- package/src/hooks/useKeyboardShortcuts.ts +1 -1
- package/src/hooks/useOrganisationPermissions.ts +9 -11
- package/src/hooks/useOrganisations.ts +13 -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 +16 -1
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
- package/src/rbac/README.md +20 -20
- 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/useRBAC.test.ts +21 -3
- package/src/rbac/hooks/useRBAC.ts +4 -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 +241 -60
- package/src/rbac/hooks/useResourcePermissions.ts +182 -63
- 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/permissions.ts +17 -17
- package/src/rbac/utils/contextValidator.ts +45 -7
- package/src/services/AuthService.ts +132 -23
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +155 -58
- 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 +4 -0
- package/src/types/database.generated.ts +4733 -3809
- 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/formatting/formatTime.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/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/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-E7YQZD7D.js +0 -175
- package/dist/DataTable-E7YQZD7D.js.map +0 -1
- package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
- package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
- package/dist/api-6LVZTHDS.js +0 -52
- package/dist/api-6LVZTHDS.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-36LVWXB2.js +0 -227
- package/dist/chunk-36LVWXB2.js.map +0 -1
- package/dist/chunk-3LPHPB62.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-5EC5MEWX.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6SOIHG6Z.js.map +0 -1
- package/dist/chunk-7JPAB3T5.js.map +0 -1
- package/dist/chunk-ATKZM7RX.js +0 -2053
- package/dist/chunk-ATKZM7RX.js.map +0 -1
- package/dist/chunk-AVMLPIM7.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.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-I6DAQMWX.js.map +0 -1
- package/dist/chunk-J36DSWQK.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-NN6WWZ5U.js.map +0 -1
- package/dist/chunk-OEWDTMG7.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-YKRAFF5K.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-OOPCLPZW.js +0 -9
- package/dist/contextValidator-OOPCLPZW.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/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/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/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/eslint-rules/pace-core-compliance.cjs +0 -510
- 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
package/scripts/audit/index.cjs
CHANGED
|
@@ -1,223 +1,1927 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @package @jmruthers/pace-core
|
|
6
|
-
* @module Audit
|
|
4
|
+
* Comprehensive Audit Script for Consuming Apps
|
|
7
5
|
*
|
|
8
|
-
*
|
|
6
|
+
* Audits consuming apps against pace-core standards and generates a markdown report.
|
|
7
|
+
* Includes:
|
|
8
|
+
* - Dependency compliance checks
|
|
9
|
+
* - Code compliance checks (future)
|
|
10
|
+
* - Markdown report generation
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node scripts/audit/index.cjs [path-to-consuming-app] [--output report.md]
|
|
14
|
+
*
|
|
15
|
+
* If no path provided, assumes current directory is consuming app.
|
|
9
16
|
*/
|
|
10
17
|
|
|
18
|
+
const fs = require('fs');
|
|
11
19
|
const path = require('path');
|
|
12
|
-
const Scanner = require('./core/scanner.cjs');
|
|
13
|
-
const { findProjectRoot, getPackageInfo, getPaceCoreVersion, colors } = require('./core/utils.cjs');
|
|
14
|
-
const consoleReporter = require('./reporters/console.cjs');
|
|
15
|
-
const markdownReporter = require('./reporters/markdown.cjs');
|
|
16
|
-
const jsonReporter = require('./reporters/json.cjs');
|
|
17
20
|
|
|
18
|
-
// Import
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const heuristicsCheck = require('./core/checks/heuristics.cjs');
|
|
25
|
-
const importsCheck = require('./core/checks/imports.cjs');
|
|
26
|
-
const hooksCheck = require('./core/checks/hooks.cjs');
|
|
27
|
-
const performanceCheck = require('./core/checks/performance.cjs');
|
|
28
|
-
const dependenciesCheck = require('./core/checks/dependencies.cjs');
|
|
29
|
-
const errorHandlingCheck = require('./core/checks/error-handling.cjs');
|
|
30
|
-
const bundleCheck = require('./core/checks/bundle.cjs');
|
|
31
|
-
const accessibilityCheck = require('./core/checks/accessibility.cjs');
|
|
32
|
-
const styleCheck = require('./core/checks/style.cjs');
|
|
33
|
-
const stateCheck = require('./core/checks/state.cjs');
|
|
34
|
-
const apiUsageCheck = require('./core/checks/api-usage.cjs');
|
|
35
|
-
const documentationCheck = require('./core/checks/documentation.cjs');
|
|
36
|
-
const testingCheck = require('./core/checks/testing.cjs');
|
|
37
|
-
const environmentCheck = require('./core/checks/environment.cjs');
|
|
38
|
-
const routesCheck = require('./core/checks/routes.cjs');
|
|
39
|
-
const formsCheck = require('./core/checks/forms.cjs');
|
|
21
|
+
// Import audit functions
|
|
22
|
+
const { runDependencyAudit } = require('./audit-dependencies.cjs');
|
|
23
|
+
const { runComponentAudit } = require('./audit-components.cjs');
|
|
24
|
+
const { runComplianceAudit } = require('./audit-compliance.cjs');
|
|
25
|
+
const { runStandardsAudit } = require('./audit-standards.cjs');
|
|
26
|
+
const { runRBACAudit } = require('./audit-rbac.cjs');
|
|
40
27
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
28
|
+
// Colors for terminal output
|
|
29
|
+
const colors = {
|
|
30
|
+
reset: '\x1b[0m',
|
|
31
|
+
red: '\x1b[31m',
|
|
32
|
+
green: '\x1b[32m',
|
|
33
|
+
yellow: '\x1b[33m',
|
|
34
|
+
blue: '\x1b[34m',
|
|
35
|
+
cyan: '\x1b[36m',
|
|
36
|
+
bold: '\x1b[1m',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Get pace-core package.json (for Vite alias checks)
|
|
40
|
+
function findPaceCorePackageJson(consumingAppPath) {
|
|
41
|
+
// Try relative to script location first (when in pace-core repo or installed package)
|
|
42
|
+
let paceCorePath = path.resolve(__dirname, '../../package.json');
|
|
43
|
+
if (fs.existsSync(paceCorePath)) {
|
|
44
|
+
return paceCorePath;
|
|
45
|
+
}
|
|
53
46
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
options.verbose = true;
|
|
65
|
-
} else if (arg === '--json') {
|
|
66
|
-
options.json = true;
|
|
67
|
-
options.reporters = ['json'];
|
|
68
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
69
|
-
printHelp();
|
|
70
|
-
process.exit(0);
|
|
71
|
-
}
|
|
47
|
+
// Try alternative location
|
|
48
|
+
paceCorePath = path.resolve(__dirname, '../../../package.json');
|
|
49
|
+
if (fs.existsSync(paceCorePath)) {
|
|
50
|
+
return paceCorePath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Try finding from consuming app's node_modules
|
|
54
|
+
const nodeModulesPath = path.join(consumingAppPath, 'node_modules', '@jmruthers', 'pace-core', 'package.json');
|
|
55
|
+
if (fs.existsSync(nodeModulesPath)) {
|
|
56
|
+
return nodeModulesPath;
|
|
72
57
|
}
|
|
73
58
|
|
|
74
|
-
return
|
|
59
|
+
return null;
|
|
75
60
|
}
|
|
76
61
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
62
|
+
// Run dependency audit and collect results (with additional checks)
|
|
63
|
+
function runDependencyAuditWithExtras(consumingAppPath, showProgress = false) {
|
|
64
|
+
// Get the base dependency audit results
|
|
65
|
+
const baseResult = runDependencyAudit(consumingAppPath);
|
|
66
|
+
|
|
67
|
+
if (baseResult.error) {
|
|
68
|
+
return baseResult;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Add Vite alias checks
|
|
72
|
+
const paceCorePath = findPaceCorePackageJson(consumingAppPath);
|
|
73
|
+
if (paceCorePath) {
|
|
74
|
+
const paceCorePkg = JSON.parse(fs.readFileSync(paceCorePath, 'utf8'));
|
|
75
|
+
const INCLUDED_DEPS = Object.keys(paceCorePkg.dependencies || {});
|
|
76
|
+
const viteAliasIssues = checkViteAliases(consumingAppPath, INCLUDED_DEPS);
|
|
77
|
+
baseResult.issues.viteAliases = viteAliasIssues;
|
|
78
|
+
} else {
|
|
79
|
+
baseResult.issues.viteAliases = [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Add component usage checks
|
|
83
|
+
if (showProgress) {
|
|
84
|
+
console.log(`${colors.blue}Running component usage audit...${colors.reset}`);
|
|
85
|
+
}
|
|
86
|
+
const componentAuditResult = runComponentAudit(consumingAppPath);
|
|
87
|
+
if (componentAuditResult.error) {
|
|
88
|
+
// If component audit fails, just log warning and continue
|
|
89
|
+
if (showProgress) {
|
|
90
|
+
console.warn(`${colors.yellow}Warning: Component audit failed: ${componentAuditResult.error}${colors.reset}`);
|
|
91
|
+
}
|
|
92
|
+
baseResult.issues.formIssues = [];
|
|
93
|
+
} else {
|
|
94
|
+
baseResult.issues.formIssues = componentAuditResult.issues.formIssues || [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Add compliance checks
|
|
98
|
+
if (showProgress) {
|
|
99
|
+
console.log(`${colors.blue}Running compliance audit...${colors.reset}`);
|
|
100
|
+
}
|
|
101
|
+
const complianceAuditResult = runComplianceAudit(consumingAppPath);
|
|
102
|
+
if (complianceAuditResult.error) {
|
|
103
|
+
// If compliance audit fails, just log warning and continue
|
|
104
|
+
if (showProgress) {
|
|
105
|
+
console.warn(`${colors.yellow}Warning: Compliance audit failed: ${complianceAuditResult.error}${colors.reset}`);
|
|
106
|
+
}
|
|
107
|
+
baseResult.issues.complianceIssues = {
|
|
108
|
+
nativeElements: [],
|
|
109
|
+
restrictedImports: [],
|
|
110
|
+
customHooks: [],
|
|
111
|
+
customUtils: [],
|
|
112
|
+
supabaseClient: [],
|
|
113
|
+
rbacSetup: [],
|
|
114
|
+
rbacPermission: [],
|
|
115
|
+
providers: [],
|
|
116
|
+
coreStyles: [],
|
|
117
|
+
inlineStyles: [],
|
|
118
|
+
};
|
|
119
|
+
} else {
|
|
120
|
+
baseResult.issues.complianceIssues = complianceAuditResult.issues || {
|
|
121
|
+
nativeElements: [],
|
|
122
|
+
restrictedImports: [],
|
|
123
|
+
customHooks: [],
|
|
124
|
+
customUtils: [],
|
|
125
|
+
supabaseClient: [],
|
|
126
|
+
rbacSetup: [],
|
|
127
|
+
rbacPermission: [],
|
|
128
|
+
providers: [],
|
|
129
|
+
coreStyles: [],
|
|
130
|
+
inlineStyles: [],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Add standards checks
|
|
135
|
+
if (showProgress) {
|
|
136
|
+
console.log(`${colors.blue}Running standards audit...${colors.reset}`);
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
const standardsAuditResult = runStandardsAudit(consumingAppPath, showProgress);
|
|
140
|
+
if (standardsAuditResult.error) {
|
|
141
|
+
// If standards audit fails, just log warning and continue
|
|
142
|
+
if (showProgress) {
|
|
143
|
+
console.warn(`\n${colors.yellow}Warning: Standards audit failed: ${standardsAuditResult.error}${colors.reset}`);
|
|
144
|
+
}
|
|
145
|
+
baseResult.issues.standardsIssues = {
|
|
146
|
+
cursorRuleset: [],
|
|
147
|
+
typescriptConfig: [],
|
|
148
|
+
anyTypes: [],
|
|
149
|
+
namingConvention: [],
|
|
150
|
+
rlsPolicy: [],
|
|
151
|
+
rpcNaming: [],
|
|
152
|
+
testingConfig: [],
|
|
153
|
+
inputValidation: [],
|
|
154
|
+
loggingSecurity: [],
|
|
155
|
+
errorMessages: [],
|
|
156
|
+
};
|
|
157
|
+
} else {
|
|
158
|
+
baseResult.issues.standardsIssues = standardsAuditResult.issues || {
|
|
159
|
+
cursorRuleset: [],
|
|
160
|
+
typescriptConfig: [],
|
|
161
|
+
anyTypes: [],
|
|
162
|
+
namingConvention: [],
|
|
163
|
+
rlsPolicy: [],
|
|
164
|
+
rpcNaming: [],
|
|
165
|
+
testingConfig: [],
|
|
166
|
+
inputValidation: [],
|
|
167
|
+
loggingSecurity: [],
|
|
168
|
+
errorMessages: [],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
// Catch any unexpected errors
|
|
173
|
+
if (showProgress) {
|
|
174
|
+
console.warn(`\n${colors.yellow}Warning: Standards audit encountered an error: ${error.message}${colors.reset}`);
|
|
175
|
+
console.warn(`${colors.yellow}Stack: ${error.stack}${colors.reset}`);
|
|
176
|
+
}
|
|
177
|
+
baseResult.issues.standardsIssues = {
|
|
178
|
+
cursorRuleset: [],
|
|
179
|
+
typescriptConfig: [],
|
|
180
|
+
anyTypes: [],
|
|
181
|
+
namingConvention: [],
|
|
182
|
+
rlsPolicy: [],
|
|
183
|
+
rpcNaming: [],
|
|
184
|
+
testingConfig: [],
|
|
185
|
+
inputValidation: [],
|
|
186
|
+
loggingSecurity: [],
|
|
187
|
+
errorMessages: [],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Add RBAC audit checks
|
|
192
|
+
if (showProgress) {
|
|
193
|
+
console.log(`${colors.blue}Running RBAC audit...${colors.reset}`);
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
const rbacAuditResult = runRBACAudit(consumingAppPath);
|
|
197
|
+
if (rbacAuditResult.error) {
|
|
198
|
+
// If RBAC audit fails, just log warning and continue
|
|
199
|
+
if (showProgress) {
|
|
200
|
+
console.warn(`\n${colors.yellow}Warning: RBAC audit failed: ${rbacAuditResult.error}${colors.reset}`);
|
|
201
|
+
}
|
|
202
|
+
baseResult.issues.rbacIssues = {
|
|
203
|
+
rbacPageGuard: [],
|
|
204
|
+
rbacWrapperComponent: [],
|
|
205
|
+
rbacWrapperFunction: [],
|
|
206
|
+
rbacResourceNames: [],
|
|
207
|
+
rbacAccessDenied: [],
|
|
208
|
+
rbacDirectRPC: [],
|
|
209
|
+
rbacDirectTable: [],
|
|
210
|
+
rbacHardcodedRole: [],
|
|
211
|
+
rbacEnforcePermissions: [],
|
|
212
|
+
rbacEdgeFunction: [],
|
|
213
|
+
};
|
|
214
|
+
} else {
|
|
215
|
+
baseResult.issues.rbacIssues = rbacAuditResult.issues || {
|
|
216
|
+
rbacPageGuard: [],
|
|
217
|
+
rbacWrapperComponent: [],
|
|
218
|
+
rbacWrapperFunction: [],
|
|
219
|
+
rbacResourceNames: [],
|
|
220
|
+
rbacAccessDenied: [],
|
|
221
|
+
rbacDirectRPC: [],
|
|
222
|
+
rbacDirectTable: [],
|
|
223
|
+
rbacHardcodedRole: [],
|
|
224
|
+
rbacEnforcePermissions: [],
|
|
225
|
+
rbacEdgeFunction: [],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
// Catch any unexpected errors
|
|
230
|
+
if (showProgress) {
|
|
231
|
+
console.warn(`\n${colors.yellow}Warning: RBAC audit encountered an error: ${error.message}${colors.reset}`);
|
|
232
|
+
console.warn(`${colors.yellow}Stack: ${error.stack}${colors.reset}`);
|
|
233
|
+
}
|
|
234
|
+
baseResult.issues.rbacIssues = {
|
|
235
|
+
rbacPageGuard: [],
|
|
236
|
+
rbacWrapperComponent: [],
|
|
237
|
+
rbacWrapperFunction: [],
|
|
238
|
+
rbacResourceNames: [],
|
|
239
|
+
rbacAccessDenied: [],
|
|
240
|
+
rbacDirectRPC: [],
|
|
241
|
+
rbacDirectTable: [],
|
|
242
|
+
rbacHardcodedRole: [],
|
|
243
|
+
rbacEnforcePermissions: [],
|
|
244
|
+
rbacEdgeFunction: [],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return baseResult;
|
|
249
|
+
}
|
|
93
250
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
251
|
+
// Check Vite config for problematic aliases
|
|
252
|
+
function checkViteAliases(consumingAppPath, includedDeps) {
|
|
253
|
+
const issues = [];
|
|
254
|
+
|
|
255
|
+
// Find vite config file
|
|
256
|
+
const viteConfigPaths = [
|
|
257
|
+
path.join(consumingAppPath, 'vite.config.ts'),
|
|
258
|
+
path.join(consumingAppPath, 'vite.config.js'),
|
|
259
|
+
path.join(consumingAppPath, 'vite.config.mjs'),
|
|
260
|
+
path.join(consumingAppPath, 'vite.config.cjs'),
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
let viteConfigPath = null;
|
|
264
|
+
for (const configPath of viteConfigPaths) {
|
|
265
|
+
if (fs.existsSync(configPath)) {
|
|
266
|
+
viteConfigPath = configPath;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!viteConfigPath) {
|
|
272
|
+
return issues; // No vite config found, skip check
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
const configContent = fs.readFileSync(viteConfigPath, 'utf8');
|
|
277
|
+
|
|
278
|
+
// Check for problematic aliases that bypass pace-core exports
|
|
279
|
+
// Pattern: "@jmruthers/pace-core/icons": "lucide-react" or similar
|
|
280
|
+
const paceCoreAliasPattern = /['"]@jmruthers\/pace-core\/(icons|utils|components|hooks|rbac)['"]\s*:\s*['"]([^'"]+)['"]/g;
|
|
281
|
+
let match;
|
|
282
|
+
|
|
283
|
+
while ((match = paceCoreAliasPattern.exec(configContent)) !== null) {
|
|
284
|
+
const paceCoreExport = match[1];
|
|
285
|
+
const aliasTarget = match[2];
|
|
286
|
+
|
|
287
|
+
// Check if alias targets an included dependency
|
|
288
|
+
if (includedDeps.includes(aliasTarget)) {
|
|
289
|
+
issues.push({
|
|
290
|
+
alias: `@jmruthers/pace-core/${paceCoreExport}`,
|
|
291
|
+
target: aliasTarget,
|
|
292
|
+
file: path.basename(viteConfigPath),
|
|
293
|
+
line: getLineNumber(configContent, match.index),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
} catch (error) {
|
|
299
|
+
// If we can't parse the config, that's okay - just skip this check
|
|
300
|
+
// Could be TypeScript, complex config, etc.
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return issues;
|
|
304
|
+
}
|
|
116
305
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
node audit/index.cjs --exclude heuristics --reporter markdown
|
|
121
|
-
node audit/index.cjs --json
|
|
122
|
-
`);
|
|
306
|
+
// Get line number from index in content
|
|
307
|
+
function getLineNumber(content, index) {
|
|
308
|
+
return content.substring(0, index).split('\n').length;
|
|
123
309
|
}
|
|
124
310
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
typescriptCheck,
|
|
150
|
-
coverageCheck,
|
|
151
|
-
heuristicsCheck,
|
|
152
|
-
importsCheck,
|
|
153
|
-
hooksCheck,
|
|
154
|
-
performanceCheck,
|
|
155
|
-
dependenciesCheck,
|
|
156
|
-
errorHandlingCheck,
|
|
157
|
-
bundleCheck,
|
|
158
|
-
accessibilityCheck,
|
|
159
|
-
styleCheck,
|
|
160
|
-
stateCheck,
|
|
161
|
-
apiUsageCheck,
|
|
162
|
-
documentationCheck,
|
|
163
|
-
testingCheck,
|
|
164
|
-
environmentCheck,
|
|
165
|
-
routesCheck,
|
|
166
|
-
formsCheck
|
|
167
|
-
]);
|
|
168
|
-
|
|
169
|
-
// Initialize scanner context
|
|
170
|
-
await scanner.initialize();
|
|
171
|
-
|
|
172
|
-
// Run checks
|
|
173
|
-
console.log(`${colors.cyan}Running checks...${colors.reset}\n`);
|
|
174
|
-
const results = await scanner.run({
|
|
175
|
-
checkNames: options.checks.length > 0 ? options.checks : null,
|
|
176
|
-
excludeChecks: options.exclude,
|
|
177
|
-
parallel: false
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Generate reports
|
|
181
|
-
const reportOptions = {
|
|
182
|
-
projectRoot,
|
|
183
|
-
packageJson,
|
|
184
|
-
paceCoreVersion,
|
|
185
|
-
verbose: options.verbose
|
|
311
|
+
// Generate markdown report
|
|
312
|
+
function generateMarkdownReport(auditResults) {
|
|
313
|
+
const { projectName, paceCoreVersion, issues } = auditResults;
|
|
314
|
+
const timestamp = new Date().toISOString();
|
|
315
|
+
|
|
316
|
+
// Calculate issue counts
|
|
317
|
+
const dependencyIssuesCount = issues.includedDeps.length +
|
|
318
|
+
issues.missingRequired.length +
|
|
319
|
+
issues.versionIssues.length +
|
|
320
|
+
issues.wrongLocation.length +
|
|
321
|
+
issues.viteAliases.length;
|
|
322
|
+
|
|
323
|
+
const componentIssuesCount = issues.formIssues?.length || 0;
|
|
324
|
+
|
|
325
|
+
const complianceIssues = issues.complianceIssues || {
|
|
326
|
+
nativeElements: [],
|
|
327
|
+
restrictedImports: [],
|
|
328
|
+
customHooks: [],
|
|
329
|
+
customUtils: [],
|
|
330
|
+
supabaseClient: [],
|
|
331
|
+
rbacSetup: [],
|
|
332
|
+
providers: [],
|
|
333
|
+
coreStyles: [],
|
|
334
|
+
inlineStyles: [],
|
|
186
335
|
};
|
|
187
336
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
337
|
+
const complianceIssuesCount = Object.values(complianceIssues).reduce((sum, arr) => sum + arr.length, 0);
|
|
338
|
+
|
|
339
|
+
const standardsIssues = issues.standardsIssues || {
|
|
340
|
+
cursorRuleset: [],
|
|
341
|
+
typescriptConfig: [],
|
|
342
|
+
anyTypes: [],
|
|
343
|
+
namingConvention: [],
|
|
344
|
+
rlsPolicy: [],
|
|
345
|
+
rpcNaming: [],
|
|
346
|
+
testingConfig: [],
|
|
347
|
+
inputValidation: [],
|
|
348
|
+
loggingSecurity: [],
|
|
349
|
+
errorMessages: [],
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const standardsIssuesCount = Object.values(standardsIssues).reduce((sum, arr) => sum + arr.length, 0);
|
|
353
|
+
|
|
354
|
+
const rbacIssues = issues.rbacIssues || {
|
|
355
|
+
rbacPageGuard: [],
|
|
356
|
+
rbacWrapperComponent: [],
|
|
357
|
+
rbacWrapperFunction: [],
|
|
358
|
+
rbacResourceNames: [],
|
|
359
|
+
rbacAccessDenied: [],
|
|
360
|
+
rbacDirectRPC: [],
|
|
361
|
+
rbacDirectTable: [],
|
|
362
|
+
rbacHardcodedRole: [],
|
|
363
|
+
rbacEnforcePermissions: [],
|
|
364
|
+
rbacEdgeFunction: [],
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const rbacIssuesCount = Object.values(rbacIssues).reduce((sum, arr) => sum + arr.length, 0);
|
|
368
|
+
|
|
369
|
+
const totalIssues = dependencyIssuesCount + componentIssuesCount + complianceIssuesCount + standardsIssuesCount + rbacIssuesCount;
|
|
370
|
+
|
|
371
|
+
let report = `# pace-core Audit Report\n\n`;
|
|
372
|
+
report += `**Project:** ${projectName}\n`;
|
|
373
|
+
report += `**pace-core Version:** ${paceCoreVersion}\n`;
|
|
374
|
+
report += `**Generated:** ${timestamp}\n\n`;
|
|
375
|
+
report += `---\n\n`;
|
|
376
|
+
|
|
377
|
+
// Summary Section (matches terminal output)
|
|
378
|
+
report += `## 📊 Summary\n\n`;
|
|
379
|
+
|
|
380
|
+
if (totalIssues === 0 && issues.missingOptional.length === 0) {
|
|
381
|
+
report += `✅ **All Checks Passed**\n\n`;
|
|
382
|
+
report += `- ✅ Dependency Audit: 0 issues\n`;
|
|
383
|
+
report += `- ✅ Component Usage Audit: 0 issues\n`;
|
|
384
|
+
report += `- ✅ Compliance Audit: 0 issues\n`;
|
|
385
|
+
report += `- ✅ Standards Audit: 0 issues\n`;
|
|
386
|
+
report += `- ✅ RBAC Audit: 0 issues\n`;
|
|
387
|
+
report += `- ✅ **Total Issues: 0**\n\n`;
|
|
388
|
+
} else {
|
|
389
|
+
report += `⚠️ **Issues Found**\n\n`;
|
|
390
|
+
|
|
391
|
+
// Dependency Audit Summary
|
|
392
|
+
if (dependencyIssuesCount === 0) {
|
|
393
|
+
report += `- ✅ Dependency Audit: 0 issues\n`;
|
|
394
|
+
} else {
|
|
395
|
+
report += `- ❌ Dependency Audit: ${dependencyIssuesCount} issue(s)\n`;
|
|
396
|
+
if (issues.includedDeps.length > 0) {
|
|
397
|
+
report += ` - Included Dependencies: ${issues.includedDeps.length}\n`;
|
|
398
|
+
}
|
|
399
|
+
if (issues.missingRequired.length > 0) {
|
|
400
|
+
report += ` - Missing Required: ${issues.missingRequired.length}\n`;
|
|
401
|
+
}
|
|
402
|
+
if (issues.versionIssues.length > 0) {
|
|
403
|
+
report += ` - Version Issues: ${issues.versionIssues.length}\n`;
|
|
404
|
+
}
|
|
405
|
+
if (issues.wrongLocation.length > 0) {
|
|
406
|
+
report += ` - Wrong Location: ${issues.wrongLocation.length}\n`;
|
|
407
|
+
}
|
|
408
|
+
if (issues.viteAliases.length > 0) {
|
|
409
|
+
report += ` - Vite Aliases: ${issues.viteAliases.length}\n`;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Component Usage Audit Summary
|
|
414
|
+
if (componentIssuesCount === 0) {
|
|
415
|
+
report += `- ✅ Component Usage Audit: 0 issues\n`;
|
|
416
|
+
} else {
|
|
417
|
+
const plainFormTagsCount = issues.formIssues.filter(i => i.type === 'plainFormTag').length;
|
|
418
|
+
const reactHookFormImportsCount = issues.formIssues.filter(i => i.type === 'reactHookFormImport').length;
|
|
419
|
+
report += `- ❌ Component Usage Audit: ${componentIssuesCount} issue(s)\n`;
|
|
420
|
+
if (plainFormTagsCount > 0) {
|
|
421
|
+
report += ` - Plain <form> tags: ${plainFormTagsCount}\n`;
|
|
422
|
+
}
|
|
423
|
+
if (reactHookFormImportsCount > 0) {
|
|
424
|
+
report += ` - Direct react-hook-form imports: ${reactHookFormImportsCount}\n`;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Compliance Audit Summary
|
|
429
|
+
if (complianceIssuesCount === 0) {
|
|
430
|
+
report += `- ✅ Compliance Audit: 0 issues\n`;
|
|
431
|
+
} else {
|
|
432
|
+
report += `- ❌ Compliance Audit: ${complianceIssuesCount} issue(s)\n`;
|
|
433
|
+
if (complianceIssues.nativeElements.length > 0) {
|
|
434
|
+
report += ` - Native HTML elements: ${complianceIssues.nativeElements.length}\n`;
|
|
435
|
+
}
|
|
436
|
+
if (complianceIssues.restrictedImports.length > 0) {
|
|
437
|
+
report += ` - Restricted imports: ${complianceIssues.restrictedImports.length}\n`;
|
|
438
|
+
}
|
|
439
|
+
if (complianceIssues.customHooks.length > 0) {
|
|
440
|
+
report += ` - Custom hooks: ${complianceIssues.customHooks.length}\n`;
|
|
441
|
+
}
|
|
442
|
+
if (complianceIssues.customUtils.length > 0) {
|
|
443
|
+
report += ` - Custom utilities: ${complianceIssues.customUtils.length}\n`;
|
|
444
|
+
}
|
|
445
|
+
if (complianceIssues.supabaseClient.length > 0) {
|
|
446
|
+
report += ` - Supabase client issues: ${complianceIssues.supabaseClient.length}\n`;
|
|
447
|
+
}
|
|
448
|
+
if (complianceIssues.rbacSetup.length > 0) {
|
|
449
|
+
report += ` - RBAC setup issues: ${complianceIssues.rbacSetup.length}\n`;
|
|
203
450
|
}
|
|
204
|
-
|
|
205
|
-
|
|
451
|
+
if (complianceIssues.rbacPermission && complianceIssues.rbacPermission.length > 0) {
|
|
452
|
+
report += ` - RBAC permission usage issues: ${complianceIssues.rbacPermission.length}\n`;
|
|
453
|
+
}
|
|
454
|
+
if (complianceIssues.providers.length > 0) {
|
|
455
|
+
report += ` - Provider issues: ${complianceIssues.providers.length}\n`;
|
|
456
|
+
}
|
|
457
|
+
if (complianceIssues.coreStyles.length > 0) {
|
|
458
|
+
report += ` - Core styles issues: ${complianceIssues.coreStyles.length}\n`;
|
|
459
|
+
}
|
|
460
|
+
if (complianceIssues.inlineStyles.length > 0) {
|
|
461
|
+
report += ` - Inline styles: ${complianceIssues.inlineStyles.length}\n`;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Standards Audit Summary
|
|
466
|
+
if (standardsIssuesCount === 0) {
|
|
467
|
+
report += `- ✅ Standards Audit: 0 issues\n`;
|
|
468
|
+
} else {
|
|
469
|
+
report += `- ❌ Standards Audit: ${standardsIssuesCount} issue(s)\n`;
|
|
470
|
+
if (standardsIssues.cursorRuleset.length > 0) {
|
|
471
|
+
report += ` - Cursor ruleset: ${standardsIssues.cursorRuleset.length}\n`;
|
|
472
|
+
}
|
|
473
|
+
if (standardsIssues.typescriptConfig.length > 0) {
|
|
474
|
+
report += ` - TypeScript config: ${standardsIssues.typescriptConfig.length}\n`;
|
|
475
|
+
}
|
|
476
|
+
if (standardsIssues.anyTypes.length > 0) {
|
|
477
|
+
report += ` - Any types: ${standardsIssues.anyTypes.length}\n`;
|
|
478
|
+
}
|
|
479
|
+
if (standardsIssues.namingConvention.length > 0) {
|
|
480
|
+
report += ` - Naming conventions: ${standardsIssues.namingConvention.length}\n`;
|
|
481
|
+
}
|
|
482
|
+
if (standardsIssues.rlsPolicy.length > 0) {
|
|
483
|
+
report += ` - RLS policies: ${standardsIssues.rlsPolicy.length}\n`;
|
|
484
|
+
}
|
|
485
|
+
if (standardsIssues.rpcNaming.length > 0) {
|
|
486
|
+
report += ` - RPC naming: ${standardsIssues.rpcNaming.length}\n`;
|
|
487
|
+
}
|
|
488
|
+
if (standardsIssues.testingConfig.length > 0) {
|
|
489
|
+
report += ` - Testing config: ${standardsIssues.testingConfig.length}\n`;
|
|
490
|
+
}
|
|
491
|
+
if (standardsIssues.inputValidation.length > 0) {
|
|
492
|
+
report += ` - Input validation: ${standardsIssues.inputValidation.length}\n`;
|
|
493
|
+
}
|
|
494
|
+
if (standardsIssues.loggingSecurity.length > 0) {
|
|
495
|
+
report += ` - Logging security: ${standardsIssues.loggingSecurity.length}\n`;
|
|
496
|
+
}
|
|
497
|
+
if (standardsIssues.errorMessages.length > 0) {
|
|
498
|
+
report += ` - Error messages: ${standardsIssues.errorMessages.length}\n`;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// RBAC Audit Summary
|
|
503
|
+
if (rbacIssuesCount === 0) {
|
|
504
|
+
report += `- ✅ RBAC Audit: 0 issues\n`;
|
|
505
|
+
} else {
|
|
506
|
+
report += `- ❌ RBAC Audit: ${rbacIssuesCount} issue(s)\n`;
|
|
507
|
+
if (rbacIssues.rbacPageGuard.length > 0) {
|
|
508
|
+
report += ` - PagePermissionGuard issues: ${rbacIssues.rbacPageGuard.length}\n`;
|
|
509
|
+
}
|
|
510
|
+
if (rbacIssues.rbacWrapperComponent.length > 0) {
|
|
511
|
+
report += ` - Wrapper components: ${rbacIssues.rbacWrapperComponent.length}\n`;
|
|
512
|
+
}
|
|
513
|
+
if (rbacIssues.rbacWrapperFunction.length > 0) {
|
|
514
|
+
report += ` - Wrapper functions: ${rbacIssues.rbacWrapperFunction.length}\n`;
|
|
515
|
+
}
|
|
516
|
+
if (rbacIssues.rbacResourceNames.length > 0) {
|
|
517
|
+
report += ` - RESOURCE_NAMES constants: ${rbacIssues.rbacResourceNames.length}\n`;
|
|
518
|
+
}
|
|
519
|
+
if (rbacIssues.rbacAccessDenied.length > 0) {
|
|
520
|
+
report += ` - AccessDenied component: ${rbacIssues.rbacAccessDenied.length}\n`;
|
|
521
|
+
}
|
|
522
|
+
if (rbacIssues.rbacDirectRPC.length > 0) {
|
|
523
|
+
report += ` - Direct RBAC RPC calls: ${rbacIssues.rbacDirectRPC.length}\n`;
|
|
524
|
+
}
|
|
525
|
+
if (rbacIssues.rbacDirectTable.length > 0) {
|
|
526
|
+
report += ` - Direct RBAC table queries: ${rbacIssues.rbacDirectTable.length}\n`;
|
|
527
|
+
}
|
|
528
|
+
if (rbacIssues.rbacHardcodedRole.length > 0) {
|
|
529
|
+
report += ` - Hardcoded role checks: ${rbacIssues.rbacHardcodedRole.length}\n`;
|
|
530
|
+
}
|
|
531
|
+
if (rbacIssues.rbacEnforcePermissions.length > 0) {
|
|
532
|
+
report += ` - enforcePermissions config: ${rbacIssues.rbacEnforcePermissions.length}\n`;
|
|
533
|
+
}
|
|
534
|
+
if (rbacIssues.rbacEdgeFunction.length > 0) {
|
|
535
|
+
report += ` - Edge Functions RBAC: ${rbacIssues.rbacEdgeFunction.length}\n`;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
report += `\n- **Total Issues: ${totalIssues}**\n\n`;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
report += `---\n\n`;
|
|
543
|
+
|
|
544
|
+
// Detailed Issues - Grouped by Audit Type
|
|
545
|
+
|
|
546
|
+
// Dependency Audit Issues
|
|
547
|
+
if (dependencyIssuesCount > 0) {
|
|
548
|
+
report += `## 📦 Dependency Audit Issues\n\n`;
|
|
549
|
+
|
|
550
|
+
// Included Dependencies
|
|
551
|
+
if (issues.includedDeps.length > 0) {
|
|
552
|
+
report += `### ❌ Included Dependencies (MUST REMOVE)\n\n`;
|
|
553
|
+
report += `These packages are already included in pace-core and should NOT be installed:\n\n`;
|
|
554
|
+
issues.includedDeps.forEach(issue => {
|
|
555
|
+
report += `- **${issue.package}**@${issue.installed} (in ${issue.location})\n`;
|
|
556
|
+
report += ` - **Problem:** Should NOT be installed (already included in pace-core)\n`;
|
|
557
|
+
report += ` - **Fix:** Run \`npm uninstall ${issue.package}\`\n\n`;
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Missing Required
|
|
562
|
+
if (issues.missingRequired.length > 0) {
|
|
563
|
+
report += `### ❌ Missing Required Dependencies\n\n`;
|
|
564
|
+
report += `These packages are required for pace-core to function:\n\n`;
|
|
565
|
+
issues.missingRequired.forEach(issue => {
|
|
566
|
+
report += `- **${issue.package}** (required: ${issue.required})\n`;
|
|
567
|
+
report += ` - **Problem:** Required dependency is missing\n`;
|
|
568
|
+
report += ` - **Fix:** Run \`npm install ${issue.package}@${issue.required}\`\n\n`;
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Version Issues
|
|
573
|
+
if (issues.versionIssues.length > 0) {
|
|
574
|
+
report += `### ⚠️ Version Issues\n\n`;
|
|
575
|
+
issues.versionIssues.forEach(issue => {
|
|
576
|
+
report += `- **${issue.package}**\n`;
|
|
577
|
+
report += ` - **Installed:** ${issue.installed}\n`;
|
|
578
|
+
report += ` - **Required:** ${issue.required}\n`;
|
|
579
|
+
report += ` - **Problem:** Version mismatch\n`;
|
|
580
|
+
report += ` - **Fix:** Run \`npm install ${issue.package}@${issue.required}\`\n\n`;
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Wrong Location
|
|
585
|
+
if (issues.wrongLocation.length > 0) {
|
|
586
|
+
report += `### ⚠️ Dependencies in Wrong Location\n\n`;
|
|
587
|
+
issues.wrongLocation.forEach(issue => {
|
|
588
|
+
report += `- **${issue.package}**\n`;
|
|
589
|
+
report += ` - **Currently in:** ${issue.current}\n`;
|
|
590
|
+
report += ` - **Should be in:** ${issue.shouldBe}\n`;
|
|
591
|
+
report += ` - **Problem:** Dependency is in the wrong section of package.json\n`;
|
|
592
|
+
report += ` - **Fix:**\n`;
|
|
593
|
+
report += ` \`\`\`bash\n`;
|
|
594
|
+
report += ` npm uninstall ${issue.package}\n`;
|
|
595
|
+
report += ` npm install -D ${issue.package}\n`;
|
|
596
|
+
report += ` \`\`\`\n\n`;
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Vite Aliases
|
|
601
|
+
if (issues.viteAliases.length > 0) {
|
|
602
|
+
report += `### ❌ Vite Aliases Bypassing pace-core Exports\n\n`;
|
|
603
|
+
report += `These Vite aliases bypass pace-core's exports and should be removed:\n\n`;
|
|
604
|
+
issues.viteAliases.forEach(issue => {
|
|
605
|
+
report += `- **${issue.alias}** → **${issue.target}**\n`;
|
|
606
|
+
report += ` - **File:** ${issue.file}${issue.line ? ` (line ${issue.line})` : ''}\n`;
|
|
607
|
+
report += ` - **Problem:** This alias bypasses pace-core's export for \`${issue.alias}\`\n`;
|
|
608
|
+
report += ` - **Fix:** Remove \`"${issue.alias}": "${issue.target}"\` from your Vite config and use \`${issue.alias}\` directly from pace-core\n\n`;
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Component Usage Audit Issues
|
|
614
|
+
if (componentIssuesCount > 0) {
|
|
615
|
+
report += `## 🧩 Component Usage Audit Issues\n\n`;
|
|
616
|
+
|
|
617
|
+
// Form Usage Issues
|
|
618
|
+
if (issues.formIssues && issues.formIssues.length > 0) {
|
|
619
|
+
report += `### ❌ Form Usage Issues (MUST FIX)\n\n`;
|
|
620
|
+
report += `These files use plain \`<form>\` tags or direct \`react-hook-form\` imports instead of pace-core Form component:\n\n`;
|
|
621
|
+
|
|
622
|
+
// Group by file for better readability
|
|
623
|
+
const issuesByFile = {};
|
|
624
|
+
issues.formIssues.forEach(issue => {
|
|
625
|
+
if (!issuesByFile[issue.file]) {
|
|
626
|
+
issuesByFile[issue.file] = [];
|
|
627
|
+
}
|
|
628
|
+
issuesByFile[issue.file].push(issue);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
632
|
+
report += `#### ${file}\n\n`;
|
|
633
|
+
fileIssues.forEach(issue => {
|
|
634
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
635
|
+
if (issue.code) {
|
|
636
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
report += `\n`;
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
report += `**Fix:** Replace with pace-core Form component. See migration guide below.\n\n`;
|
|
206
643
|
}
|
|
207
644
|
}
|
|
208
645
|
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
|
|
646
|
+
// Compliance Audit Issues
|
|
647
|
+
if (complianceIssuesCount > 0) {
|
|
648
|
+
report += `## 🔒 Compliance Audit Issues\n\n`;
|
|
649
|
+
|
|
650
|
+
// Native Elements
|
|
651
|
+
if (complianceIssues.nativeElements.length > 0) {
|
|
652
|
+
report += `### ❌ Native HTML Elements (MUST FIX)\n\n`;
|
|
653
|
+
report += `These files use native HTML elements when pace-core components exist:\n\n`;
|
|
654
|
+
|
|
655
|
+
const issuesByFile = {};
|
|
656
|
+
complianceIssues.nativeElements.forEach(issue => {
|
|
657
|
+
if (!issuesByFile[issue.file]) {
|
|
658
|
+
issuesByFile[issue.file] = [];
|
|
659
|
+
}
|
|
660
|
+
issuesByFile[issue.file].push(issue);
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
664
|
+
report += `#### ${file}\n\n`;
|
|
665
|
+
fileIssues.forEach(issue => {
|
|
666
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
667
|
+
if (issue.code) {
|
|
668
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
669
|
+
}
|
|
670
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Restricted Imports
|
|
676
|
+
if (complianceIssues.restrictedImports.length > 0) {
|
|
677
|
+
report += `### ❌ Restricted Library Imports (MUST FIX)\n\n`;
|
|
678
|
+
report += `These files import directly from restricted libraries. Use pace-core wrappers instead:\n\n`;
|
|
679
|
+
|
|
680
|
+
const issuesByFile = {};
|
|
681
|
+
complianceIssues.restrictedImports.forEach(issue => {
|
|
682
|
+
if (!issuesByFile[issue.file]) {
|
|
683
|
+
issuesByFile[issue.file] = [];
|
|
684
|
+
}
|
|
685
|
+
issuesByFile[issue.file].push(issue);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
689
|
+
report += `#### ${file}\n\n`;
|
|
690
|
+
fileIssues.forEach(issue => {
|
|
691
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
692
|
+
if (issue.code) {
|
|
693
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
694
|
+
}
|
|
695
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
696
|
+
});
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Custom Hooks
|
|
701
|
+
if (complianceIssues.customHooks.length > 0) {
|
|
702
|
+
report += `### ⚠️ Custom Hooks (Consider Using pace-core)\n\n`;
|
|
703
|
+
report += `These files define custom hooks that pace-core provides:\n\n`;
|
|
704
|
+
|
|
705
|
+
const issuesByFile = {};
|
|
706
|
+
complianceIssues.customHooks.forEach(issue => {
|
|
707
|
+
if (!issuesByFile[issue.file]) {
|
|
708
|
+
issuesByFile[issue.file] = [];
|
|
709
|
+
}
|
|
710
|
+
issuesByFile[issue.file].push(issue);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
714
|
+
report += `#### ${file}\n\n`;
|
|
715
|
+
fileIssues.forEach(issue => {
|
|
716
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
717
|
+
if (issue.code) {
|
|
718
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
719
|
+
}
|
|
720
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Custom Utils
|
|
726
|
+
if (complianceIssues.customUtils.length > 0) {
|
|
727
|
+
report += `### ⚠️ Custom Utilities (Consider Using pace-core)\n\n`;
|
|
728
|
+
report += `These files define custom utilities that pace-core provides:\n\n`;
|
|
729
|
+
|
|
730
|
+
const issuesByFile = {};
|
|
731
|
+
complianceIssues.customUtils.forEach(issue => {
|
|
732
|
+
if (!issuesByFile[issue.file]) {
|
|
733
|
+
issuesByFile[issue.file] = [];
|
|
734
|
+
}
|
|
735
|
+
issuesByFile[issue.file].push(issue);
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
739
|
+
report += `#### ${file}\n\n`;
|
|
740
|
+
fileIssues.forEach(issue => {
|
|
741
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
742
|
+
if (issue.code) {
|
|
743
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
744
|
+
}
|
|
745
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Supabase Client
|
|
751
|
+
if (complianceIssues.supabaseClient.length > 0) {
|
|
752
|
+
report += `### ❌ Supabase Client Usage Issues (MUST FIX)\n\n`;
|
|
753
|
+
report += `These files have Supabase client usage issues:\n\n`;
|
|
754
|
+
|
|
755
|
+
const issuesByFile = {};
|
|
756
|
+
complianceIssues.supabaseClient.forEach(issue => {
|
|
757
|
+
if (!issuesByFile[issue.file]) {
|
|
758
|
+
issuesByFile[issue.file] = [];
|
|
759
|
+
}
|
|
760
|
+
issuesByFile[issue.file].push(issue);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
764
|
+
report += `#### ${file}\n\n`;
|
|
765
|
+
fileIssues.forEach(issue => {
|
|
766
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
767
|
+
if (issue.code) {
|
|
768
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
769
|
+
}
|
|
770
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
771
|
+
});
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// RBAC Permission Usage
|
|
776
|
+
if (complianceIssues.rbacPermission && complianceIssues.rbacPermission.length > 0) {
|
|
777
|
+
report += `### ❌ RBAC Permission Usage Issues (MUST FIX)\n\n`;
|
|
778
|
+
report += `These issues can cause false negative permission checks when scope resolution is still in progress.\n\n`;
|
|
779
|
+
complianceIssues.rbacPermission.forEach(issue => {
|
|
780
|
+
report += `**${issue.file}:${issue.line}** - ${issue.severity.toUpperCase()}\n`;
|
|
781
|
+
report += `${issue.message}\n\n`;
|
|
782
|
+
if (issue.fix) {
|
|
783
|
+
report += `**Fix:** ${issue.fix}\n\n`;
|
|
784
|
+
}
|
|
785
|
+
if (issue.code) {
|
|
786
|
+
report += `\`\`\`typescript\n${issue.code}\n\`\`\`\n\n`;
|
|
787
|
+
}
|
|
788
|
+
report += `---\n\n`;
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// RBAC Setup
|
|
793
|
+
if (complianceIssues.rbacSetup.length > 0) {
|
|
794
|
+
report += `### ❌ RBAC Setup Issues (MUST FIX)\n\n`;
|
|
795
|
+
complianceIssues.rbacSetup.forEach(issue => {
|
|
796
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
797
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Providers
|
|
802
|
+
if (complianceIssues.providers.length > 0) {
|
|
803
|
+
report += `### ❌ Provider Usage Issues (MUST FIX)\n\n`;
|
|
804
|
+
complianceIssues.providers.forEach(issue => {
|
|
805
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
806
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Core Styles
|
|
811
|
+
if (complianceIssues.coreStyles.length > 0) {
|
|
812
|
+
report += `### ❌ Core Styles Import Issues (MUST FIX)\n\n`;
|
|
813
|
+
complianceIssues.coreStyles.forEach(issue => {
|
|
814
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
815
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Inline Styles
|
|
820
|
+
if (complianceIssues.inlineStyles.length > 0) {
|
|
821
|
+
report += `### ❌ Inline Styles (MUST FIX)\n\n`;
|
|
822
|
+
report += `These files use inline styles. Use pace-core components or Tailwind classes instead:\n\n`;
|
|
823
|
+
|
|
824
|
+
const issuesByFile = {};
|
|
825
|
+
complianceIssues.inlineStyles.forEach(issue => {
|
|
826
|
+
if (!issuesByFile[issue.file]) {
|
|
827
|
+
issuesByFile[issue.file] = [];
|
|
828
|
+
}
|
|
829
|
+
issuesByFile[issue.file].push(issue);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
833
|
+
report += `#### ${file}\n\n`;
|
|
834
|
+
fileIssues.forEach(issue => {
|
|
835
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
836
|
+
if (issue.code) {
|
|
837
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
838
|
+
}
|
|
839
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
840
|
+
});
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Standards Audit Issues
|
|
846
|
+
if (standardsIssuesCount > 0) {
|
|
847
|
+
report += `## 📋 Standards Audit Issues\n\n`;
|
|
848
|
+
|
|
849
|
+
// Cursor Ruleset
|
|
850
|
+
if (standardsIssues.cursorRuleset.length > 0) {
|
|
851
|
+
report += `### ❌ Cursor Ruleset Issues (MUST FIX)\n\n`;
|
|
852
|
+
report += `Required rule files are missing from .cursor/rules/ directory:\n\n`;
|
|
853
|
+
standardsIssues.cursorRuleset.forEach(issue => {
|
|
854
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
855
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// TypeScript Config
|
|
860
|
+
if (standardsIssues.typescriptConfig.length > 0) {
|
|
861
|
+
report += `### ❌ TypeScript Configuration Issues (MUST FIX)\n\n`;
|
|
862
|
+
standardsIssues.typescriptConfig.forEach(issue => {
|
|
863
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
864
|
+
if (issue.code) {
|
|
865
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
866
|
+
}
|
|
867
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Any Types
|
|
872
|
+
if (standardsIssues.anyTypes.length > 0) {
|
|
873
|
+
report += `### ⚠️ Any Types Detected\n\n`;
|
|
874
|
+
report += `These files use 'any' types. Consider using 'unknown' or proper types:\n\n`;
|
|
875
|
+
|
|
876
|
+
const issuesByFile = {};
|
|
877
|
+
standardsIssues.anyTypes.forEach(issue => {
|
|
878
|
+
if (!issuesByFile[issue.file]) {
|
|
879
|
+
issuesByFile[issue.file] = [];
|
|
880
|
+
}
|
|
881
|
+
issuesByFile[issue.file].push(issue);
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
885
|
+
report += `#### ${file}\n\n`;
|
|
886
|
+
fileIssues.forEach(issue => {
|
|
887
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
888
|
+
if (issue.code) {
|
|
889
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
890
|
+
}
|
|
891
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
892
|
+
});
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Naming Conventions
|
|
897
|
+
if (standardsIssues.namingConvention.length > 0) {
|
|
898
|
+
report += `### ⚠️ Naming Convention Issues\n\n`;
|
|
899
|
+
report += `These files have naming convention violations:\n\n`;
|
|
900
|
+
|
|
901
|
+
const issuesByFile = {};
|
|
902
|
+
standardsIssues.namingConvention.forEach(issue => {
|
|
903
|
+
if (!issuesByFile[issue.file]) {
|
|
904
|
+
issuesByFile[issue.file] = [];
|
|
905
|
+
}
|
|
906
|
+
issuesByFile[issue.file].push(issue);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
910
|
+
report += `#### ${file}\n\n`;
|
|
911
|
+
fileIssues.forEach(issue => {
|
|
912
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
913
|
+
if (issue.code) {
|
|
914
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
915
|
+
}
|
|
916
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// RLS Policies
|
|
922
|
+
if (standardsIssues.rlsPolicy.length > 0) {
|
|
923
|
+
report += `### ❌ RLS Policy Issues (MUST FIX)\n\n`;
|
|
924
|
+
report += `These SQL migrations have RLS policy violations:\n\n`;
|
|
925
|
+
|
|
926
|
+
const issuesByFile = {};
|
|
927
|
+
standardsIssues.rlsPolicy.forEach(issue => {
|
|
928
|
+
if (!issuesByFile[issue.file]) {
|
|
929
|
+
issuesByFile[issue.file] = [];
|
|
930
|
+
}
|
|
931
|
+
issuesByFile[issue.file].push(issue);
|
|
932
|
+
});
|
|
933
|
+
|
|
934
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
935
|
+
report += `#### ${file}\n\n`;
|
|
936
|
+
fileIssues.forEach(issue => {
|
|
937
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
938
|
+
if (issue.code) {
|
|
939
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
940
|
+
}
|
|
941
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
942
|
+
});
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// RPC Naming
|
|
947
|
+
if (standardsIssues.rpcNaming.length > 0) {
|
|
948
|
+
report += `### ❌ RPC Naming Issues (MUST FIX)\n\n`;
|
|
949
|
+
report += `These SQL migrations have RPC function naming violations:\n\n`;
|
|
950
|
+
|
|
951
|
+
const issuesByFile = {};
|
|
952
|
+
standardsIssues.rpcNaming.forEach(issue => {
|
|
953
|
+
if (!issuesByFile[issue.file]) {
|
|
954
|
+
issuesByFile[issue.file] = [];
|
|
955
|
+
}
|
|
956
|
+
issuesByFile[issue.file].push(issue);
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
960
|
+
report += `#### ${file}\n\n`;
|
|
961
|
+
fileIssues.forEach(issue => {
|
|
962
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
963
|
+
if (issue.code) {
|
|
964
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
965
|
+
}
|
|
966
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
967
|
+
});
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Testing Config
|
|
972
|
+
if (standardsIssues.testingConfig.length > 0) {
|
|
973
|
+
report += `### ❌ Testing Configuration Issues (MUST FIX)\n\n`;
|
|
974
|
+
standardsIssues.testingConfig.forEach(issue => {
|
|
975
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
976
|
+
if (issue.code) {
|
|
977
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
978
|
+
}
|
|
979
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// Input Validation (heuristic)
|
|
984
|
+
if (standardsIssues.inputValidation.length > 0) {
|
|
985
|
+
report += `### ⚠️ Input Validation Issues (Heuristic)\n\n`;
|
|
986
|
+
report += `These files may be missing Zod validation. This is a heuristic check and may have false positives:\n\n`;
|
|
987
|
+
|
|
988
|
+
const issuesByFile = {};
|
|
989
|
+
standardsIssues.inputValidation.forEach(issue => {
|
|
990
|
+
if (!issuesByFile[issue.file]) {
|
|
991
|
+
issuesByFile[issue.file] = [];
|
|
992
|
+
}
|
|
993
|
+
issuesByFile[issue.file].push(issue);
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
997
|
+
report += `#### ${file}\n\n`;
|
|
998
|
+
fileIssues.forEach(issue => {
|
|
999
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1000
|
+
if (issue.code) {
|
|
1001
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1002
|
+
}
|
|
1003
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1004
|
+
});
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// Logging Security (heuristic)
|
|
1009
|
+
if (standardsIssues.loggingSecurity.length > 0) {
|
|
1010
|
+
report += `### ⚠️ Logging Security Issues (Heuristic)\n\n`;
|
|
1011
|
+
report += `These files may be logging sensitive data. This is a heuristic check and may have false positives:\n\n`;
|
|
1012
|
+
|
|
1013
|
+
const issuesByFile = {};
|
|
1014
|
+
standardsIssues.loggingSecurity.forEach(issue => {
|
|
1015
|
+
if (!issuesByFile[issue.file]) {
|
|
1016
|
+
issuesByFile[issue.file] = [];
|
|
1017
|
+
}
|
|
1018
|
+
issuesByFile[issue.file].push(issue);
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1022
|
+
report += `#### ${file}\n\n`;
|
|
1023
|
+
fileIssues.forEach(issue => {
|
|
1024
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1025
|
+
if (issue.code) {
|
|
1026
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1027
|
+
}
|
|
1028
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1029
|
+
});
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// Error Messages (heuristic)
|
|
1034
|
+
if (standardsIssues.errorMessages.length > 0) {
|
|
1035
|
+
report += `### ⚠️ Error Message Safety Issues (Heuristic)\n\n`;
|
|
1036
|
+
report += `These files may expose internal details in error messages. This is a heuristic check and may have false positives:\n\n`;
|
|
1037
|
+
|
|
1038
|
+
const issuesByFile = {};
|
|
1039
|
+
standardsIssues.errorMessages.forEach(issue => {
|
|
1040
|
+
if (!issuesByFile[issue.file]) {
|
|
1041
|
+
issuesByFile[issue.file] = [];
|
|
1042
|
+
}
|
|
1043
|
+
issuesByFile[issue.file].push(issue);
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1047
|
+
report += `#### ${file}\n\n`;
|
|
1048
|
+
fileIssues.forEach(issue => {
|
|
1049
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1050
|
+
if (issue.code) {
|
|
1051
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1052
|
+
}
|
|
1053
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1054
|
+
});
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// RBAC Audit Issues
|
|
1060
|
+
if (rbacIssuesCount > 0) {
|
|
1061
|
+
report += `## 🔐 RBAC Audit Issues\n\n`;
|
|
1062
|
+
report += `These issues relate to RBAC (Role-Based Access Control) compliance. See [09-rbac-compliance.mdc](../../packages/core/cursor-rules/09-rbac-compliance.mdc) for complete documentation.\n\n`;
|
|
1063
|
+
|
|
1064
|
+
// PagePermissionGuard Issues
|
|
1065
|
+
if (rbacIssues.rbacPageGuard.length > 0) {
|
|
1066
|
+
report += `### ❌ PagePermissionGuard Issues (MUST FIX)\n\n`;
|
|
1067
|
+
report += `These pages are missing PagePermissionGuard wrapper or using it incorrectly:\n\n`;
|
|
1068
|
+
|
|
1069
|
+
const issuesByFile = {};
|
|
1070
|
+
rbacIssues.rbacPageGuard.forEach(issue => {
|
|
1071
|
+
if (!issuesByFile[issue.file]) {
|
|
1072
|
+
issuesByFile[issue.file] = [];
|
|
1073
|
+
}
|
|
1074
|
+
issuesByFile[issue.file].push(issue);
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1078
|
+
report += `#### ${file}\n\n`;
|
|
1079
|
+
fileIssues.forEach(issue => {
|
|
1080
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1081
|
+
if (issue.code) {
|
|
1082
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1083
|
+
}
|
|
1084
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1085
|
+
});
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// Wrapper Components
|
|
1090
|
+
if (rbacIssues.rbacWrapperComponent.length > 0) {
|
|
1091
|
+
report += `### ❌ Wrapper Components Around PagePermissionGuard (MUST FIX)\n\n`;
|
|
1092
|
+
report += `These components wrap PagePermissionGuard, which is forbidden:\n\n`;
|
|
1093
|
+
|
|
1094
|
+
const issuesByFile = {};
|
|
1095
|
+
rbacIssues.rbacWrapperComponent.forEach(issue => {
|
|
1096
|
+
if (!issuesByFile[issue.file]) {
|
|
1097
|
+
issuesByFile[issue.file] = [];
|
|
1098
|
+
}
|
|
1099
|
+
issuesByFile[issue.file].push(issue);
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1103
|
+
report += `#### ${file}\n\n`;
|
|
1104
|
+
fileIssues.forEach(issue => {
|
|
1105
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1106
|
+
if (issue.code) {
|
|
1107
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1108
|
+
}
|
|
1109
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1110
|
+
});
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// Wrapper Functions
|
|
1115
|
+
if (rbacIssues.rbacWrapperFunction.length > 0) {
|
|
1116
|
+
report += `### ❌ Wrapper Functions Around Permission Hooks (MUST FIX)\n\n`;
|
|
1117
|
+
report += `These functions wrap permission hooks, which is forbidden:\n\n`;
|
|
1118
|
+
|
|
1119
|
+
const issuesByFile = {};
|
|
1120
|
+
rbacIssues.rbacWrapperFunction.forEach(issue => {
|
|
1121
|
+
if (!issuesByFile[issue.file]) {
|
|
1122
|
+
issuesByFile[issue.file] = [];
|
|
1123
|
+
}
|
|
1124
|
+
issuesByFile[issue.file].push(issue);
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1128
|
+
report += `#### ${file}\n\n`;
|
|
1129
|
+
fileIssues.forEach(issue => {
|
|
1130
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1131
|
+
if (issue.code) {
|
|
1132
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1133
|
+
}
|
|
1134
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// RESOURCE_NAMES Constants
|
|
1140
|
+
if (rbacIssues.rbacResourceNames.length > 0) {
|
|
1141
|
+
report += `### ❌ RESOURCE_NAMES Constants Usage (MUST FIX)\n\n`;
|
|
1142
|
+
report += `These files use string literals in useResourcePermissions instead of RESOURCE_NAMES constants:\n\n`;
|
|
1143
|
+
|
|
1144
|
+
const issuesByFile = {};
|
|
1145
|
+
rbacIssues.rbacResourceNames.forEach(issue => {
|
|
1146
|
+
if (!issuesByFile[issue.file]) {
|
|
1147
|
+
issuesByFile[issue.file] = [];
|
|
1148
|
+
}
|
|
1149
|
+
issuesByFile[issue.file].push(issue);
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1153
|
+
report += `#### ${file}\n\n`;
|
|
1154
|
+
fileIssues.forEach(issue => {
|
|
1155
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1156
|
+
if (issue.code) {
|
|
1157
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1158
|
+
}
|
|
1159
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1160
|
+
});
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// AccessDenied Component
|
|
1165
|
+
if (rbacIssues.rbacAccessDenied.length > 0) {
|
|
1166
|
+
report += `### ⚠️ AccessDenied Component Usage\n\n`;
|
|
1167
|
+
report += `These files use custom access denied components instead of pace-core AccessDenied:\n\n`;
|
|
1168
|
+
|
|
1169
|
+
const issuesByFile = {};
|
|
1170
|
+
rbacIssues.rbacAccessDenied.forEach(issue => {
|
|
1171
|
+
if (!issuesByFile[issue.file]) {
|
|
1172
|
+
issuesByFile[issue.file] = [];
|
|
1173
|
+
}
|
|
1174
|
+
issuesByFile[issue.file].push(issue);
|
|
1175
|
+
});
|
|
1176
|
+
|
|
1177
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1178
|
+
report += `#### ${file}\n\n`;
|
|
1179
|
+
fileIssues.forEach(issue => {
|
|
1180
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1181
|
+
if (issue.code) {
|
|
1182
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1183
|
+
}
|
|
1184
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1185
|
+
});
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// Direct RBAC RPC Calls
|
|
1190
|
+
if (rbacIssues.rbacDirectRPC.length > 0) {
|
|
1191
|
+
report += `### ❌ Direct RBAC RPC Calls (SECURITY CRITICAL)\n\n`;
|
|
1192
|
+
report += `These files call RBAC RPC functions directly, bypassing security validation:\n\n`;
|
|
1193
|
+
|
|
1194
|
+
const issuesByFile = {};
|
|
1195
|
+
rbacIssues.rbacDirectRPC.forEach(issue => {
|
|
1196
|
+
if (!issuesByFile[issue.file]) {
|
|
1197
|
+
issuesByFile[issue.file] = [];
|
|
1198
|
+
}
|
|
1199
|
+
issuesByFile[issue.file].push(issue);
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1203
|
+
report += `#### ${file}\n\n`;
|
|
1204
|
+
fileIssues.forEach(issue => {
|
|
1205
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1206
|
+
if (issue.code) {
|
|
1207
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1208
|
+
}
|
|
1209
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1210
|
+
});
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// Direct RBAC Table Queries
|
|
1215
|
+
if (rbacIssues.rbacDirectTable.length > 0) {
|
|
1216
|
+
report += `### ❌ Direct RBAC Table Queries (SECURITY CRITICAL)\n\n`;
|
|
1217
|
+
report += `These files query RBAC tables directly without useSecureSupabase:\n\n`;
|
|
1218
|
+
|
|
1219
|
+
const issuesByFile = {};
|
|
1220
|
+
rbacIssues.rbacDirectTable.forEach(issue => {
|
|
1221
|
+
if (!issuesByFile[issue.file]) {
|
|
1222
|
+
issuesByFile[issue.file] = [];
|
|
1223
|
+
}
|
|
1224
|
+
issuesByFile[issue.file].push(issue);
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1228
|
+
report += `#### ${file}\n\n`;
|
|
1229
|
+
fileIssues.forEach(issue => {
|
|
1230
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1231
|
+
if (issue.code) {
|
|
1232
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1233
|
+
}
|
|
1234
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1235
|
+
});
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// Hardcoded Role Checks
|
|
1240
|
+
if (rbacIssues.rbacHardcodedRole.length > 0) {
|
|
1241
|
+
report += `### ❌ Hardcoded Role Checks (SECURITY CRITICAL)\n\n`;
|
|
1242
|
+
report += `These files use hardcoded role comparisons instead of pace-core APIs:\n\n`;
|
|
1243
|
+
|
|
1244
|
+
const issuesByFile = {};
|
|
1245
|
+
rbacIssues.rbacHardcodedRole.forEach(issue => {
|
|
1246
|
+
if (!issuesByFile[issue.file]) {
|
|
1247
|
+
issuesByFile[issue.file] = [];
|
|
1248
|
+
}
|
|
1249
|
+
issuesByFile[issue.file].push(issue);
|
|
1250
|
+
});
|
|
1251
|
+
|
|
1252
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1253
|
+
report += `#### ${file}\n\n`;
|
|
1254
|
+
fileIssues.forEach(issue => {
|
|
1255
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1256
|
+
if (issue.code) {
|
|
1257
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1258
|
+
}
|
|
1259
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1260
|
+
});
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// enforcePermissions Configuration
|
|
1265
|
+
if (rbacIssues.rbacEnforcePermissions.length > 0) {
|
|
1266
|
+
report += `### ❌ enforcePermissions Configuration Issues\n\n`;
|
|
1267
|
+
report += `These files have incorrect enforcePermissions configuration:\n\n`;
|
|
1268
|
+
|
|
1269
|
+
rbacIssues.rbacEnforcePermissions.forEach(issue => {
|
|
1270
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
1271
|
+
if (issue.code) {
|
|
1272
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1273
|
+
}
|
|
1274
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Edge Functions RBAC
|
|
1279
|
+
if (rbacIssues.rbacEdgeFunction.length > 0) {
|
|
1280
|
+
report += `### ❌ Edge Functions RBAC Issues (SECURITY CRITICAL)\n\n`;
|
|
1281
|
+
report += `These Edge Functions have RBAC compliance issues:\n\n`;
|
|
1282
|
+
|
|
1283
|
+
rbacIssues.rbacEdgeFunction.forEach(issue => {
|
|
1284
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
1285
|
+
if (issue.code) {
|
|
1286
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1287
|
+
}
|
|
1288
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
// RBAC Audit Issues
|
|
1294
|
+
if (rbacIssuesCount > 0) {
|
|
1295
|
+
report += `## 🔐 RBAC Audit Issues\n\n`;
|
|
1296
|
+
report += `These issues relate to RBAC (Role-Based Access Control) compliance. See [09-rbac-compliance.mdc](../../packages/core/cursor-rules/09-rbac-compliance.mdc) for complete documentation.\n\n`;
|
|
1297
|
+
|
|
1298
|
+
// PagePermissionGuard Issues
|
|
1299
|
+
if (rbacIssues.rbacPageGuard.length > 0) {
|
|
1300
|
+
report += `### ❌ PagePermissionGuard Issues (MUST FIX)\n\n`;
|
|
1301
|
+
report += `These pages are missing PagePermissionGuard wrapper or using it incorrectly:\n\n`;
|
|
1302
|
+
|
|
1303
|
+
const issuesByFile = {};
|
|
1304
|
+
rbacIssues.rbacPageGuard.forEach(issue => {
|
|
1305
|
+
if (!issuesByFile[issue.file]) {
|
|
1306
|
+
issuesByFile[issue.file] = [];
|
|
1307
|
+
}
|
|
1308
|
+
issuesByFile[issue.file].push(issue);
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1312
|
+
report += `#### ${file}\n\n`;
|
|
1313
|
+
fileIssues.forEach(issue => {
|
|
1314
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1315
|
+
if (issue.code) {
|
|
1316
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1317
|
+
}
|
|
1318
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// Wrapper Components
|
|
1324
|
+
if (rbacIssues.rbacWrapperComponent.length > 0) {
|
|
1325
|
+
report += `### ❌ Wrapper Components Around PagePermissionGuard (MUST FIX)\n\n`;
|
|
1326
|
+
report += `These components wrap PagePermissionGuard, which is forbidden:\n\n`;
|
|
1327
|
+
|
|
1328
|
+
const issuesByFile = {};
|
|
1329
|
+
rbacIssues.rbacWrapperComponent.forEach(issue => {
|
|
1330
|
+
if (!issuesByFile[issue.file]) {
|
|
1331
|
+
issuesByFile[issue.file] = [];
|
|
1332
|
+
}
|
|
1333
|
+
issuesByFile[issue.file].push(issue);
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1337
|
+
report += `#### ${file}\n\n`;
|
|
1338
|
+
fileIssues.forEach(issue => {
|
|
1339
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1340
|
+
if (issue.code) {
|
|
1341
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1342
|
+
}
|
|
1343
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1344
|
+
});
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// Wrapper Functions
|
|
1349
|
+
if (rbacIssues.rbacWrapperFunction.length > 0) {
|
|
1350
|
+
report += `### ❌ Wrapper Functions Around Permission Hooks (MUST FIX)\n\n`;
|
|
1351
|
+
report += `These functions wrap permission hooks, which is forbidden:\n\n`;
|
|
1352
|
+
|
|
1353
|
+
const issuesByFile = {};
|
|
1354
|
+
rbacIssues.rbacWrapperFunction.forEach(issue => {
|
|
1355
|
+
if (!issuesByFile[issue.file]) {
|
|
1356
|
+
issuesByFile[issue.file] = [];
|
|
1357
|
+
}
|
|
1358
|
+
issuesByFile[issue.file].push(issue);
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1362
|
+
report += `#### ${file}\n\n`;
|
|
1363
|
+
fileIssues.forEach(issue => {
|
|
1364
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1365
|
+
if (issue.code) {
|
|
1366
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1367
|
+
}
|
|
1368
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1369
|
+
});
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
// RESOURCE_NAMES Constants
|
|
1374
|
+
if (rbacIssues.rbacResourceNames.length > 0) {
|
|
1375
|
+
report += `### ❌ RESOURCE_NAMES Constants Usage (MUST FIX)\n\n`;
|
|
1376
|
+
report += `These files use string literals in useResourcePermissions instead of RESOURCE_NAMES constants:\n\n`;
|
|
1377
|
+
|
|
1378
|
+
const issuesByFile = {};
|
|
1379
|
+
rbacIssues.rbacResourceNames.forEach(issue => {
|
|
1380
|
+
if (!issuesByFile[issue.file]) {
|
|
1381
|
+
issuesByFile[issue.file] = [];
|
|
1382
|
+
}
|
|
1383
|
+
issuesByFile[issue.file].push(issue);
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1387
|
+
report += `#### ${file}\n\n`;
|
|
1388
|
+
fileIssues.forEach(issue => {
|
|
1389
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1390
|
+
if (issue.code) {
|
|
1391
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1392
|
+
}
|
|
1393
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1394
|
+
});
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
// AccessDenied Component
|
|
1399
|
+
if (rbacIssues.rbacAccessDenied.length > 0) {
|
|
1400
|
+
report += `### ⚠️ AccessDenied Component Usage\n\n`;
|
|
1401
|
+
report += `These files use custom access denied components instead of pace-core AccessDenied:\n\n`;
|
|
1402
|
+
|
|
1403
|
+
const issuesByFile = {};
|
|
1404
|
+
rbacIssues.rbacAccessDenied.forEach(issue => {
|
|
1405
|
+
if (!issuesByFile[issue.file]) {
|
|
1406
|
+
issuesByFile[issue.file] = [];
|
|
1407
|
+
}
|
|
1408
|
+
issuesByFile[issue.file].push(issue);
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1412
|
+
report += `#### ${file}\n\n`;
|
|
1413
|
+
fileIssues.forEach(issue => {
|
|
1414
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1415
|
+
if (issue.code) {
|
|
1416
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1417
|
+
}
|
|
1418
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1419
|
+
});
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
// Direct RBAC RPC Calls
|
|
1424
|
+
if (rbacIssues.rbacDirectRPC.length > 0) {
|
|
1425
|
+
report += `### ❌ Direct RBAC RPC Calls (SECURITY CRITICAL)\n\n`;
|
|
1426
|
+
report += `These files call RBAC RPC functions directly, bypassing security validation:\n\n`;
|
|
1427
|
+
|
|
1428
|
+
const issuesByFile = {};
|
|
1429
|
+
rbacIssues.rbacDirectRPC.forEach(issue => {
|
|
1430
|
+
if (!issuesByFile[issue.file]) {
|
|
1431
|
+
issuesByFile[issue.file] = [];
|
|
1432
|
+
}
|
|
1433
|
+
issuesByFile[issue.file].push(issue);
|
|
1434
|
+
});
|
|
1435
|
+
|
|
1436
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1437
|
+
report += `#### ${file}\n\n`;
|
|
1438
|
+
fileIssues.forEach(issue => {
|
|
1439
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1440
|
+
if (issue.code) {
|
|
1441
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1442
|
+
}
|
|
1443
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1444
|
+
});
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// Direct RBAC Table Queries
|
|
1449
|
+
if (rbacIssues.rbacDirectTable.length > 0) {
|
|
1450
|
+
report += `### ❌ Direct RBAC Table Queries (SECURITY CRITICAL)\n\n`;
|
|
1451
|
+
report += `These files query RBAC tables directly without useSecureSupabase:\n\n`;
|
|
1452
|
+
|
|
1453
|
+
const issuesByFile = {};
|
|
1454
|
+
rbacIssues.rbacDirectTable.forEach(issue => {
|
|
1455
|
+
if (!issuesByFile[issue.file]) {
|
|
1456
|
+
issuesByFile[issue.file] = [];
|
|
1457
|
+
}
|
|
1458
|
+
issuesByFile[issue.file].push(issue);
|
|
1459
|
+
});
|
|
1460
|
+
|
|
1461
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1462
|
+
report += `#### ${file}\n\n`;
|
|
1463
|
+
fileIssues.forEach(issue => {
|
|
1464
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1465
|
+
if (issue.code) {
|
|
1466
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1467
|
+
}
|
|
1468
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1469
|
+
});
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
// Hardcoded Role Checks
|
|
1474
|
+
if (rbacIssues.rbacHardcodedRole.length > 0) {
|
|
1475
|
+
report += `### ❌ Hardcoded Role Checks (SECURITY CRITICAL)\n\n`;
|
|
1476
|
+
report += `These files use hardcoded role comparisons instead of pace-core APIs:\n\n`;
|
|
1477
|
+
|
|
1478
|
+
const issuesByFile = {};
|
|
1479
|
+
rbacIssues.rbacHardcodedRole.forEach(issue => {
|
|
1480
|
+
if (!issuesByFile[issue.file]) {
|
|
1481
|
+
issuesByFile[issue.file] = [];
|
|
1482
|
+
}
|
|
1483
|
+
issuesByFile[issue.file].push(issue);
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
Object.entries(issuesByFile).forEach(([file, fileIssues]) => {
|
|
1487
|
+
report += `#### ${file}\n\n`;
|
|
1488
|
+
fileIssues.forEach(issue => {
|
|
1489
|
+
report += `- **Line ${issue.line}**: ${issue.message}\n`;
|
|
1490
|
+
if (issue.code) {
|
|
1491
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1492
|
+
}
|
|
1493
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1494
|
+
});
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// enforcePermissions Configuration
|
|
1499
|
+
if (rbacIssues.rbacEnforcePermissions.length > 0) {
|
|
1500
|
+
report += `### ❌ enforcePermissions Configuration Issues\n\n`;
|
|
1501
|
+
report += `These files have incorrect enforcePermissions configuration:\n\n`;
|
|
1502
|
+
|
|
1503
|
+
rbacIssues.rbacEnforcePermissions.forEach(issue => {
|
|
1504
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
1505
|
+
if (issue.code) {
|
|
1506
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1507
|
+
}
|
|
1508
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
// Edge Functions RBAC
|
|
1513
|
+
if (rbacIssues.rbacEdgeFunction.length > 0) {
|
|
1514
|
+
report += `### ❌ Edge Functions RBAC Issues (SECURITY CRITICAL)\n\n`;
|
|
1515
|
+
report += `These Edge Functions have RBAC compliance issues:\n\n`;
|
|
1516
|
+
|
|
1517
|
+
rbacIssues.rbacEdgeFunction.forEach(issue => {
|
|
1518
|
+
report += `- **${issue.file}** (Line ${issue.line}): ${issue.message}\n`;
|
|
1519
|
+
if (issue.code) {
|
|
1520
|
+
report += ` \`\`\`\n ${issue.code}\n \`\`\`\n`;
|
|
1521
|
+
}
|
|
1522
|
+
report += ` - **Fix:** ${issue.fix}\n\n`;
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// Missing Optional (informational only)
|
|
1528
|
+
if (issues.missingOptional.length > 0) {
|
|
1529
|
+
report += `---\n\n`;
|
|
1530
|
+
report += `## ℹ️ Missing Optional Dependencies\n\n`;
|
|
1531
|
+
report += `These packages are optional (only install if you use these features):\n\n`;
|
|
1532
|
+
issues.missingOptional.forEach(issue => {
|
|
1533
|
+
report += `- **${issue.package}** (required: ${issue.required})\n`;
|
|
1534
|
+
});
|
|
1535
|
+
report += `\n`;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
// Fix Commands Section
|
|
1539
|
+
if (totalIssues > 0) {
|
|
1540
|
+
report += `---\n\n`;
|
|
1541
|
+
report += `## 🔧 Quick Fix Commands\n\n`;
|
|
1542
|
+
report += `Run these commands to fix the issues found:\n\n`;
|
|
1543
|
+
|
|
1544
|
+
if (issues.includedDeps.length > 0) {
|
|
1545
|
+
const depsToRemove = issues.includedDeps.map(i => i.package).join(' ');
|
|
1546
|
+
report += `### Remove Included Dependencies\n\n`;
|
|
1547
|
+
report += `\`\`\`bash\n`;
|
|
1548
|
+
report += `npm uninstall ${depsToRemove}\n`;
|
|
1549
|
+
report += `\`\`\`\n\n`;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
if (issues.missingRequired.length > 0) {
|
|
1553
|
+
const depsToInstall = issues.missingRequired
|
|
1554
|
+
.map(i => `${i.package}@${i.required}`)
|
|
1555
|
+
.join(' ');
|
|
1556
|
+
report += `### Install Missing Required Dependencies\n\n`;
|
|
1557
|
+
report += `\`\`\`bash\n`;
|
|
1558
|
+
report += `npm install ${depsToInstall}\n`;
|
|
1559
|
+
report += `\`\`\`\n\n`;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
if (issues.versionIssues.length > 0) {
|
|
1563
|
+
const depsToFix = issues.versionIssues
|
|
1564
|
+
.map(i => `${i.package}@${i.required}`)
|
|
1565
|
+
.join(' ');
|
|
1566
|
+
report += `### Fix Version Ranges\n\n`;
|
|
1567
|
+
report += `\`\`\`bash\n`;
|
|
1568
|
+
report += `npm install ${depsToFix}\n`;
|
|
1569
|
+
report += `\`\`\`\n\n`;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
if (issues.wrongLocation.length > 0) {
|
|
1573
|
+
issues.wrongLocation.forEach(issue => {
|
|
1574
|
+
report += `### Move ${issue.package} to devDependencies\n\n`;
|
|
1575
|
+
report += `\`\`\`bash\n`;
|
|
1576
|
+
report += `npm uninstall ${issue.package}\n`;
|
|
1577
|
+
report += `npm install -D ${issue.package}\n`;
|
|
1578
|
+
report += `\`\`\`\n\n`;
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
if (issues.viteAliases.length > 0) {
|
|
1583
|
+
report += `### Fix Vite Aliases\n\n`;
|
|
1584
|
+
report += `Remove the following aliases from your \`vite.config.ts\` (or \`vite.config.js\`):\n\n`;
|
|
1585
|
+
issues.viteAliases.forEach(issue => {
|
|
1586
|
+
report += `- Remove: \`"${issue.alias}": "${issue.target}"\`\n`;
|
|
1587
|
+
});
|
|
1588
|
+
report += `\n`;
|
|
1589
|
+
report += `These aliases bypass pace-core's exports. Use the pace-core exports directly instead.\n\n`;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
if (issues.formIssues && issues.formIssues.length > 0) {
|
|
1593
|
+
report += `### Fix Form Usage\n\n`;
|
|
1594
|
+
report += `Replace plain \`<form>\` tags and direct \`react-hook-form\` usage with pace-core Form component:\n\n`;
|
|
1595
|
+
report += `**Before (❌ Wrong):**\n`;
|
|
1596
|
+
report += `\`\`\`tsx\n`;
|
|
1597
|
+
report += `import { useForm } from 'react-hook-form';\n`;
|
|
1598
|
+
report += `\n`;
|
|
1599
|
+
report += `function MyForm() {\n`;
|
|
1600
|
+
report += ` const { register, handleSubmit } = useForm();\n`;
|
|
1601
|
+
report += ` return (\n`;
|
|
1602
|
+
report += ` <form onSubmit={handleSubmit(onSubmit)}>\n`;
|
|
1603
|
+
report += ` <input {...register('name')} />\n`;
|
|
1604
|
+
report += ` <button type="submit">Submit</button>\n`;
|
|
1605
|
+
report += ` </form>\n`;
|
|
1606
|
+
report += ` );\n`;
|
|
1607
|
+
report += `}\n`;
|
|
1608
|
+
report += `\`\`\`\n\n`;
|
|
1609
|
+
report += `**After (✅ Correct):**\n`;
|
|
1610
|
+
report += `\`\`\`tsx\n`;
|
|
1611
|
+
report += `import { Form, FormField, Button } from '@jmruthers/pace-core';\n`;
|
|
1612
|
+
report += `import { z } from 'zod';\n`;
|
|
1613
|
+
report += `\n`;
|
|
1614
|
+
report += `const formSchema = z.object({\n`;
|
|
1615
|
+
report += ` name: z.string().min(1, 'Name is required'),\n`;
|
|
1616
|
+
report += `});\n`;
|
|
1617
|
+
report += `\n`;
|
|
1618
|
+
report += `function MyForm() {\n`;
|
|
1619
|
+
report += ` return (\n`;
|
|
1620
|
+
report += ` <Form\n`;
|
|
1621
|
+
report += ` schema={formSchema}\n`;
|
|
1622
|
+
report += ` defaultValues={{ name: '' }}\n`;
|
|
1623
|
+
report += ` onSubmit={(data) => console.log(data)}\n`;
|
|
1624
|
+
report += ` >\n`;
|
|
1625
|
+
report += ` <FormField name="name" label="Name" />\n`;
|
|
1626
|
+
report += ` <Button type="submit">Submit</Button>\n`;
|
|
1627
|
+
report += ` </Form>\n`;
|
|
1628
|
+
report += ` );\n`;
|
|
1629
|
+
report += `}\n`;
|
|
1630
|
+
report += `\`\`\`\n\n`;
|
|
1631
|
+
report += `**Key Benefits:**\n`;
|
|
1632
|
+
report += `- Built-in Zod validation with type safety\n`;
|
|
1633
|
+
report += `- Automatic form persistence (session draft)\n`;
|
|
1634
|
+
report += `- Consistent styling with pace-core design system\n`;
|
|
1635
|
+
report += `- Accessible form structure with error handling\n`;
|
|
1636
|
+
report += `- Sensitive field filtering (passwords, etc.)\n\n`;
|
|
1637
|
+
report += `**Documentation:** See [pace-core Forms Guide](https://github.com/jmruthers/pace-core/blob/main/packages/core/docs/implementation-guides/forms.md) for complete examples.\n\n`;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
report += `---\n\n`;
|
|
1642
|
+
report += `*Generated by pace-core audit script*\n`;
|
|
1643
|
+
|
|
1644
|
+
return report;
|
|
212
1645
|
}
|
|
213
1646
|
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
1647
|
+
// Main function
|
|
1648
|
+
function main() {
|
|
1649
|
+
const args = process.argv.slice(2);
|
|
1650
|
+
const consumingAppPath = args.find(arg => !arg.startsWith('--')) || process.cwd();
|
|
1651
|
+
const outputArg = args.find(arg => arg.startsWith('--output'));
|
|
1652
|
+
const outputPath = outputArg ? outputArg.split('=')[1] || 'audit-report.md' : null;
|
|
1653
|
+
|
|
1654
|
+
console.log(`${colors.bold}${colors.cyan}pace-core Comprehensive Audit${colors.reset}\n`);
|
|
1655
|
+
console.log(`${colors.cyan}${'='.repeat(50)}${colors.reset}\n`);
|
|
1656
|
+
|
|
1657
|
+
// Run dependency audit (with Vite alias checks and component audit)
|
|
1658
|
+
console.log(`${colors.blue}Running dependency audit...${colors.reset}`);
|
|
1659
|
+
const auditResults = runDependencyAuditWithExtras(consumingAppPath, true);
|
|
1660
|
+
|
|
1661
|
+
if (auditResults.error) {
|
|
1662
|
+
console.error(`${colors.red}Error: ${auditResults.error}${colors.reset}`);
|
|
219
1663
|
process.exit(1);
|
|
220
|
-
}
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// Display results
|
|
1667
|
+
const { projectName, paceCoreVersion, issues } = auditResults;
|
|
1668
|
+
|
|
1669
|
+
// Calculate issue counts
|
|
1670
|
+
const dependencyIssuesCount = issues.includedDeps.length +
|
|
1671
|
+
issues.missingRequired.length +
|
|
1672
|
+
issues.versionIssues.length +
|
|
1673
|
+
issues.wrongLocation.length +
|
|
1674
|
+
issues.viteAliases.length;
|
|
1675
|
+
|
|
1676
|
+
const componentIssuesCount = issues.formIssues?.length || 0;
|
|
1677
|
+
|
|
1678
|
+
const complianceIssues = issues.complianceIssues || {
|
|
1679
|
+
nativeElements: [],
|
|
1680
|
+
restrictedImports: [],
|
|
1681
|
+
customHooks: [],
|
|
1682
|
+
customUtils: [],
|
|
1683
|
+
supabaseClient: [],
|
|
1684
|
+
rbacSetup: [],
|
|
1685
|
+
providers: [],
|
|
1686
|
+
coreStyles: [],
|
|
1687
|
+
inlineStyles: [],
|
|
1688
|
+
};
|
|
1689
|
+
|
|
1690
|
+
const complianceIssuesCount = Object.values(complianceIssues).reduce((sum, arr) => sum + arr.length, 0);
|
|
1691
|
+
|
|
1692
|
+
const standardsIssues = issues.standardsIssues || {
|
|
1693
|
+
cursorRuleset: [],
|
|
1694
|
+
typescriptConfig: [],
|
|
1695
|
+
anyTypes: [],
|
|
1696
|
+
namingConvention: [],
|
|
1697
|
+
rlsPolicy: [],
|
|
1698
|
+
rpcNaming: [],
|
|
1699
|
+
testingConfig: [],
|
|
1700
|
+
inputValidation: [],
|
|
1701
|
+
loggingSecurity: [],
|
|
1702
|
+
errorMessages: [],
|
|
1703
|
+
};
|
|
1704
|
+
|
|
1705
|
+
const standardsIssuesCount = Object.values(standardsIssues).reduce((sum, arr) => sum + arr.length, 0);
|
|
1706
|
+
|
|
1707
|
+
const rbacIssues = issues.rbacIssues || {
|
|
1708
|
+
rbacPageGuard: [],
|
|
1709
|
+
rbacWrapperComponent: [],
|
|
1710
|
+
rbacWrapperFunction: [],
|
|
1711
|
+
rbacResourceNames: [],
|
|
1712
|
+
rbacAccessDenied: [],
|
|
1713
|
+
rbacDirectRPC: [],
|
|
1714
|
+
rbacDirectTable: [],
|
|
1715
|
+
rbacHardcodedRole: [],
|
|
1716
|
+
rbacEnforcePermissions: [],
|
|
1717
|
+
rbacEdgeFunction: [],
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1720
|
+
const rbacIssuesCount = Object.values(rbacIssues).reduce((sum, arr) => sum + arr.length, 0);
|
|
1721
|
+
|
|
1722
|
+
const totalIssues = dependencyIssuesCount + componentIssuesCount + complianceIssuesCount + standardsIssuesCount + rbacIssuesCount;
|
|
1723
|
+
|
|
1724
|
+
// Display project info
|
|
1725
|
+
console.log(`Project: ${colors.bold}${projectName}${colors.reset}`);
|
|
1726
|
+
console.log(`pace-core: ${colors.bold}${paceCoreVersion}${colors.reset}\n`);
|
|
1727
|
+
|
|
1728
|
+
// Display audit results summary
|
|
1729
|
+
console.log(`${colors.bold}Audit Results:${colors.reset}\n`);
|
|
1730
|
+
|
|
1731
|
+
// Dependency Audit
|
|
1732
|
+
if (dependencyIssuesCount === 0) {
|
|
1733
|
+
console.log(` ${colors.green}✅ Dependency Audit: 0 issues${colors.reset}`);
|
|
1734
|
+
} else {
|
|
1735
|
+
console.log(` ${colors.red}❌ Dependency Audit: ${dependencyIssuesCount} issue(s)${colors.reset}`);
|
|
1736
|
+
if (issues.includedDeps.length > 0) {
|
|
1737
|
+
console.log(` - Included Dependencies: ${issues.includedDeps.length}`);
|
|
1738
|
+
}
|
|
1739
|
+
if (issues.missingRequired.length > 0) {
|
|
1740
|
+
console.log(` - Missing Required: ${issues.missingRequired.length}`);
|
|
1741
|
+
}
|
|
1742
|
+
if (issues.versionIssues.length > 0) {
|
|
1743
|
+
console.log(` - Version Issues: ${issues.versionIssues.length}`);
|
|
1744
|
+
}
|
|
1745
|
+
if (issues.wrongLocation.length > 0) {
|
|
1746
|
+
console.log(` - Wrong Location: ${issues.wrongLocation.length}`);
|
|
1747
|
+
}
|
|
1748
|
+
if (issues.viteAliases.length > 0) {
|
|
1749
|
+
console.log(` - Vite Aliases: ${issues.viteAliases.length}`);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
// Component Usage Audit
|
|
1754
|
+
if (componentIssuesCount === 0) {
|
|
1755
|
+
console.log(` ${colors.green}✅ Component Usage Audit: 0 issues${colors.reset}`);
|
|
1756
|
+
} else {
|
|
1757
|
+
const plainFormTagsCount = issues.formIssues.filter(i => i.type === 'plainFormTag').length;
|
|
1758
|
+
const reactHookFormImportsCount = issues.formIssues.filter(i => i.type === 'reactHookFormImport').length;
|
|
1759
|
+
console.log(` ${colors.red}❌ Component Usage Audit: ${componentIssuesCount} issue(s)${colors.reset}`);
|
|
1760
|
+
if (plainFormTagsCount > 0) {
|
|
1761
|
+
console.log(` - Plain <form> tags: ${plainFormTagsCount}`);
|
|
1762
|
+
}
|
|
1763
|
+
if (reactHookFormImportsCount > 0) {
|
|
1764
|
+
console.log(` - Direct react-hook-form imports: ${reactHookFormImportsCount}`);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
// Compliance Audit
|
|
1769
|
+
if (complianceIssuesCount === 0) {
|
|
1770
|
+
console.log(` ${colors.green}✅ Compliance Audit: 0 issues${colors.reset}`);
|
|
1771
|
+
} else {
|
|
1772
|
+
console.log(` ${colors.red}❌ Compliance Audit: ${complianceIssuesCount} issue(s)${colors.reset}`);
|
|
1773
|
+
if (complianceIssues.nativeElements.length > 0) {
|
|
1774
|
+
console.log(` - Native HTML elements: ${complianceIssues.nativeElements.length}`);
|
|
1775
|
+
}
|
|
1776
|
+
if (complianceIssues.restrictedImports.length > 0) {
|
|
1777
|
+
console.log(` - Restricted imports: ${complianceIssues.restrictedImports.length}`);
|
|
1778
|
+
}
|
|
1779
|
+
if (complianceIssues.customHooks.length > 0) {
|
|
1780
|
+
console.log(` - Custom hooks: ${complianceIssues.customHooks.length}`);
|
|
1781
|
+
}
|
|
1782
|
+
if (complianceIssues.customUtils.length > 0) {
|
|
1783
|
+
console.log(` - Custom utilities: ${complianceIssues.customUtils.length}`);
|
|
1784
|
+
}
|
|
1785
|
+
if (complianceIssues.supabaseClient.length > 0) {
|
|
1786
|
+
console.log(` - Supabase client issues: ${complianceIssues.supabaseClient.length}`);
|
|
1787
|
+
}
|
|
1788
|
+
if (complianceIssues.rbacSetup.length > 0) {
|
|
1789
|
+
console.log(` - RBAC setup issues: ${complianceIssues.rbacSetup.length}`);
|
|
1790
|
+
}
|
|
1791
|
+
if (complianceIssues.rbacPermission && complianceIssues.rbacPermission.length > 0) {
|
|
1792
|
+
console.log(` - RBAC permission usage issues: ${complianceIssues.rbacPermission.length}`);
|
|
1793
|
+
}
|
|
1794
|
+
if (complianceIssues.providers.length > 0) {
|
|
1795
|
+
console.log(` - Provider issues: ${complianceIssues.providers.length}`);
|
|
1796
|
+
}
|
|
1797
|
+
if (complianceIssues.coreStyles.length > 0) {
|
|
1798
|
+
console.log(` - Core styles issues: ${complianceIssues.coreStyles.length}`);
|
|
1799
|
+
}
|
|
1800
|
+
if (complianceIssues.inlineStyles.length > 0) {
|
|
1801
|
+
console.log(` - Inline styles: ${complianceIssues.inlineStyles.length}`);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// Standards Audit
|
|
1806
|
+
if (standardsIssuesCount === 0) {
|
|
1807
|
+
console.log(` ${colors.green}✅ Standards Audit: 0 issues${colors.reset}`);
|
|
1808
|
+
} else {
|
|
1809
|
+
console.log(` ${colors.red}❌ Standards Audit: ${standardsIssuesCount} issue(s)${colors.reset}`);
|
|
1810
|
+
if (standardsIssues.cursorRuleset.length > 0) {
|
|
1811
|
+
console.log(` - Cursor ruleset: ${standardsIssues.cursorRuleset.length}`);
|
|
1812
|
+
}
|
|
1813
|
+
if (standardsIssues.typescriptConfig.length > 0) {
|
|
1814
|
+
console.log(` - TypeScript config: ${standardsIssues.typescriptConfig.length}`);
|
|
1815
|
+
}
|
|
1816
|
+
if (standardsIssues.anyTypes.length > 0) {
|
|
1817
|
+
console.log(` - Any types: ${standardsIssues.anyTypes.length}`);
|
|
1818
|
+
}
|
|
1819
|
+
if (standardsIssues.namingConvention.length > 0) {
|
|
1820
|
+
console.log(` - Naming conventions: ${standardsIssues.namingConvention.length}`);
|
|
1821
|
+
}
|
|
1822
|
+
if (standardsIssues.rlsPolicy.length > 0) {
|
|
1823
|
+
console.log(` - RLS policies: ${standardsIssues.rlsPolicy.length}`);
|
|
1824
|
+
}
|
|
1825
|
+
if (standardsIssues.rpcNaming.length > 0) {
|
|
1826
|
+
console.log(` - RPC naming: ${standardsIssues.rpcNaming.length}`);
|
|
1827
|
+
}
|
|
1828
|
+
if (standardsIssues.testingConfig.length > 0) {
|
|
1829
|
+
console.log(` - Testing config: ${standardsIssues.testingConfig.length}`);
|
|
1830
|
+
}
|
|
1831
|
+
if (standardsIssues.inputValidation.length > 0) {
|
|
1832
|
+
console.log(` - Input validation: ${standardsIssues.inputValidation.length}`);
|
|
1833
|
+
}
|
|
1834
|
+
if (standardsIssues.loggingSecurity.length > 0) {
|
|
1835
|
+
console.log(` - Logging security: ${standardsIssues.loggingSecurity.length}`);
|
|
1836
|
+
}
|
|
1837
|
+
if (standardsIssues.errorMessages.length > 0) {
|
|
1838
|
+
console.log(` - Error messages: ${standardsIssues.errorMessages.length}`);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// RBAC Audit
|
|
1843
|
+
if (rbacIssuesCount === 0) {
|
|
1844
|
+
console.log(` ${colors.green}✅ RBAC Audit: 0 issues${colors.reset}`);
|
|
1845
|
+
} else {
|
|
1846
|
+
console.log(` ${colors.red}❌ RBAC Audit: ${rbacIssuesCount} issue(s)${colors.reset}`);
|
|
1847
|
+
if (rbacIssues.rbacPageGuard.length > 0) {
|
|
1848
|
+
console.log(` - PagePermissionGuard issues: ${rbacIssues.rbacPageGuard.length}`);
|
|
1849
|
+
}
|
|
1850
|
+
if (rbacIssues.rbacWrapperComponent.length > 0) {
|
|
1851
|
+
console.log(` - Wrapper components: ${rbacIssues.rbacWrapperComponent.length}`);
|
|
1852
|
+
}
|
|
1853
|
+
if (rbacIssues.rbacWrapperFunction.length > 0) {
|
|
1854
|
+
console.log(` - Wrapper functions: ${rbacIssues.rbacWrapperFunction.length}`);
|
|
1855
|
+
}
|
|
1856
|
+
if (rbacIssues.rbacResourceNames.length > 0) {
|
|
1857
|
+
console.log(` - RESOURCE_NAMES constants: ${rbacIssues.rbacResourceNames.length}`);
|
|
1858
|
+
}
|
|
1859
|
+
if (rbacIssues.rbacAccessDenied.length > 0) {
|
|
1860
|
+
console.log(` - AccessDenied component: ${rbacIssues.rbacAccessDenied.length}`);
|
|
1861
|
+
}
|
|
1862
|
+
if (rbacIssues.rbacDirectRPC.length > 0) {
|
|
1863
|
+
console.log(` - Direct RBAC RPC calls: ${rbacIssues.rbacDirectRPC.length}`);
|
|
1864
|
+
}
|
|
1865
|
+
if (rbacIssues.rbacDirectTable.length > 0) {
|
|
1866
|
+
console.log(` - Direct RBAC table queries: ${rbacIssues.rbacDirectTable.length}`);
|
|
1867
|
+
}
|
|
1868
|
+
if (rbacIssues.rbacHardcodedRole.length > 0) {
|
|
1869
|
+
console.log(` - Hardcoded role checks: ${rbacIssues.rbacHardcodedRole.length}`);
|
|
1870
|
+
}
|
|
1871
|
+
if (rbacIssues.rbacEnforcePermissions.length > 0) {
|
|
1872
|
+
console.log(` - enforcePermissions config: ${rbacIssues.rbacEnforcePermissions.length}`);
|
|
1873
|
+
}
|
|
1874
|
+
if (rbacIssues.rbacEdgeFunction.length > 0) {
|
|
1875
|
+
console.log(` - Edge Functions RBAC: ${rbacIssues.rbacEdgeFunction.length}`);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
// Total summary
|
|
1880
|
+
console.log(`\n${colors.bold}Total Issues: ${totalIssues === 0 ? colors.green + '0 (All checks passed!)' : colors.red + totalIssues}${colors.reset}\n`);
|
|
1881
|
+
|
|
1882
|
+
// Generate and save markdown report
|
|
1883
|
+
const markdownReport = generateMarkdownReport(auditResults);
|
|
1884
|
+
|
|
1885
|
+
// Generate timestamp in yyyymmddHHMM format
|
|
1886
|
+
const now = new Date();
|
|
1887
|
+
const year = now.getFullYear();
|
|
1888
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
1889
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
1890
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
1891
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
1892
|
+
const timestamp = `${year}${month}${day}${hours}${minutes}`;
|
|
1893
|
+
|
|
1894
|
+
// Helper function to add timestamp to filename
|
|
1895
|
+
function addTimestampToFilename(filePath) {
|
|
1896
|
+
const dir = path.dirname(filePath);
|
|
1897
|
+
const ext = path.extname(filePath);
|
|
1898
|
+
const name = path.basename(filePath, ext);
|
|
1899
|
+
return path.join(dir, `${name}-${timestamp}${ext}`);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
// Determine report path
|
|
1903
|
+
let reportPath;
|
|
1904
|
+
if (outputPath) {
|
|
1905
|
+
reportPath = addTimestampToFilename(path.join(consumingAppPath, outputPath));
|
|
1906
|
+
} else {
|
|
1907
|
+
// Default: save to audit/ directory
|
|
1908
|
+
const auditDir = path.join(consumingAppPath, 'audit');
|
|
1909
|
+
if (!fs.existsSync(auditDir)) {
|
|
1910
|
+
fs.mkdirSync(auditDir, { recursive: true });
|
|
1911
|
+
}
|
|
1912
|
+
reportPath = path.join(auditDir, `pace-core-audit-${timestamp}.md`);
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// Save report
|
|
1916
|
+
fs.writeFileSync(reportPath, markdownReport, 'utf8');
|
|
1917
|
+
|
|
1918
|
+
// Display report location
|
|
1919
|
+
const relativeReportPath = path.relative(consumingAppPath, reportPath);
|
|
1920
|
+
console.log(`${colors.bold}Report saved to:${colors.reset} ${colors.cyan}${relativeReportPath}${colors.reset}\n`);
|
|
1921
|
+
|
|
1922
|
+
// Exit with error code if issues found
|
|
1923
|
+
process.exit(totalIssues > 0 ? 1 : 0);
|
|
221
1924
|
}
|
|
222
1925
|
|
|
223
|
-
|
|
1926
|
+
main();
|
|
1927
|
+
|