@oneuptime/common 12.0.2 → 12.0.4
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/Models/DatabaseModels/CephCluster.ts +14 -0
- package/Models/DatabaseModels/CodeRepository.ts +14 -6
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/DockerHost.ts +14 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/IoTFleet.ts +14 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/NetworkDevice.ts +14 -0
- package/Models/DatabaseModels/NetworkInterface.ts +18 -0
- package/Models/DatabaseModels/Project.ts +16 -2
- package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
- package/Models/DatabaseModels/Service.ts +30 -0
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Models/DatabaseModels/User.ts +30 -0
- package/Server/API/AIAgentDataAPI.ts +31 -0
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +458 -12
- package/Server/API/GitHubAPI.ts +119 -283
- package/Server/API/UserAPI.ts +263 -1
- package/Server/EnvironmentConfig.ts +38 -4
- package/Server/Infrastructure/GlobalCache.ts +152 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/AnalyticsDatabaseService.ts +72 -1
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/CodeRepositoryService.ts +105 -2
- package/Server/Services/DatabaseService.ts +296 -2
- package/Server/Services/DockerHostService.ts +19 -79
- package/Server/Services/DockerSwarmClusterService.ts +27 -88
- package/Server/Services/HostService.ts +46 -99
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetService.ts +19 -79
- package/Server/Services/KubernetesClusterService.ts +17 -78
- package/Server/Services/LabelService.ts +57 -10
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +89 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/OpenTelemetryIngestService.ts +230 -26
- package/Server/Services/PodmanHostService.ts +19 -79
- package/Server/Services/ProxmoxClusterService.ts +23 -83
- package/Server/Services/RumApplicationService.ts +19 -80
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +24 -85
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Services/UserService.ts +184 -0
- package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
- package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Monitor/MonitorResource.ts +63 -36
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
- package/Server/Utils/PasswordHash.ts +306 -0
- package/Server/Utils/SingleFlight.ts +83 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
- package/Server/Utils/Telemetry/Telemetry.ts +351 -122
- package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
- package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
- package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
- package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
- package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
- package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
- package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
- package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
- package/Tests/Server/API/Helpers.ts +6 -1
- package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
- package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
- package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
- package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
- package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
- package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
- package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
- package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
- package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
- package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
- package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
- package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
- package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
- package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
- package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
- package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
- package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
- package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
- package/Tests/Types/Database/ColumnLength.test.ts +7 -1
- package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
- package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
- package/Tests/Types/Decimal.test.ts +37 -0
- package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
- package/Types/Dashboard/DashboardTemplates.ts +397 -0
- package/Types/Database/BigIntColumnTransformer.ts +74 -0
- package/Types/Database/ColumnLength.ts +9 -1
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Database/TableColumn.ts +11 -0
- package/Types/Database/UnsynchronizedIndex.ts +48 -0
- package/Types/Decimal.ts +16 -4
- package/Types/HashedString.ts +139 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/UI/Components/Navbar/NavBar.tsx +12 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
- package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/Utils/TeamMembersByProject.ts +237 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +16 -2
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +28 -1
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +32 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +343 -10
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GitHubAPI.js +94 -167
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +187 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +34 -4
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -76
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -73
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
- package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +246 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -69
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -102
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -69
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +42 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MetricTypeService.js +74 -0
- package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +81 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -69
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -70
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +236 -61
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +165 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
- package/build/dist/Server/Utils/PasswordHash.js +237 -0
- package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
- package/build/dist/Server/Utils/SingleFlight.js +76 -0
- package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
- package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
- package/build/dist/Types/Database/ColumnLength.js +9 -1
- package/build/dist/Types/Database/ColumnLength.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +25 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/NumericColumnValue.js +97 -0
- package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
- package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/HashedString.js +95 -5
- package/build/dist/Types/HashedString.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Port.js +15 -5
- package/build/dist/Types/Port.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
- package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/TeamMembersByProject.js +145 -0
- package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
- package/package.json +1 -1
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
RepoResolution,
|
|
18
18
|
} from "../../CodeRepository/StackTraceRepoResolver";
|
|
19
19
|
import logger from "../../Logger";
|
|
20
|
+
import GitHubInstallationBinding from "../../CodeRepository/GitHub/GitHubInstallationBinding";
|
|
20
21
|
import StackTraceParser, {
|
|
21
22
|
ParsedStackTrace,
|
|
22
23
|
StackFrame,
|
|
@@ -121,6 +122,7 @@ export async function findReadableRepositories(
|
|
|
121
122
|
query: query as never,
|
|
122
123
|
select: {
|
|
123
124
|
_id: true,
|
|
125
|
+
projectId: true,
|
|
124
126
|
name: true,
|
|
125
127
|
organizationName: true,
|
|
126
128
|
repositoryName: true,
|
|
@@ -134,11 +136,50 @@ export async function findReadableRepositories(
|
|
|
134
136
|
props: ctx.props,
|
|
135
137
|
});
|
|
136
138
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
const gitHubRepositories: Array<CodeRepository> = repositories.filter(
|
|
140
|
+
(repository: CodeRepository) => {
|
|
141
|
+
return (
|
|
142
|
+
repository.repositoryHostedAt === CodeRepositoryType.GitHub &&
|
|
143
|
+
Boolean(repository.gitHubAppInstallationId)
|
|
144
|
+
);
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
/*
|
|
149
|
+
* Every code tool — read and write — resolves its target through here, so
|
|
150
|
+
* this is the one place that has to prove the row's installation really
|
|
151
|
+
* belongs to the row's project (GHSA-xx95-gmcf-7q86). "The row is in your
|
|
152
|
+
* project" is not that proof: before the fix a caller could put any
|
|
153
|
+
* installation ID on a row in a project they owned, and these tools would
|
|
154
|
+
* then read source, commit and open pull requests in another tenant's
|
|
155
|
+
* private repositories.
|
|
156
|
+
*
|
|
157
|
+
* Writing an unbound installation ID is closed off now and the quarantine
|
|
158
|
+
* migration clears the rows that already existed, so this filter should
|
|
159
|
+
* never drop anything on a healthy database. It stays because the cost of
|
|
160
|
+
* being wrong here is another tenant's source code.
|
|
161
|
+
*/
|
|
162
|
+
if (gitHubRepositories.length === 0) {
|
|
163
|
+
return gitHubRepositories;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Every row here is in ctx.projectId, so one lookup answers for all of them.
|
|
167
|
+
const boundInstallationId: string | null =
|
|
168
|
+
await GitHubInstallationBinding.getBoundInstallationId(ctx.projectId);
|
|
169
|
+
|
|
170
|
+
return gitHubRepositories.filter((repository: CodeRepository) => {
|
|
171
|
+
if (
|
|
172
|
+
boundInstallationId &&
|
|
173
|
+
repository.gitHubAppInstallationId === boundInstallationId
|
|
174
|
+
) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
logger.error(
|
|
179
|
+
`Excluding code repository ${repository.id?.toString()} from AI code tools: its GitHub App installation is not bound to project ${ctx.projectId.toString()}.`,
|
|
141
180
|
);
|
|
181
|
+
|
|
182
|
+
return false;
|
|
142
183
|
});
|
|
143
184
|
}
|
|
144
185
|
|
|
@@ -1169,11 +1169,24 @@ export default class StatementGenerator<TBaseModel extends AnalyticsBaseModel> {
|
|
|
1169
1169
|
public toSortStatement(sort: Sort<TBaseModel>): Statement {
|
|
1170
1170
|
const sortStatement: Statement = new Statement();
|
|
1171
1171
|
|
|
1172
|
+
/*
|
|
1173
|
+
* Keys must be comma separated. Without the separator a multi-key sort
|
|
1174
|
+
* concatenates into a single malformed term (`time DESCseverity ASC`),
|
|
1175
|
+
* so every ORDER BY beyond the first key was unusable. Mirrors
|
|
1176
|
+
* toGroupByStatement above.
|
|
1177
|
+
*/
|
|
1178
|
+
let first: boolean = true;
|
|
1172
1179
|
for (const key in sort) {
|
|
1173
1180
|
if (!this.model.getTableColumn(key)) {
|
|
1174
1181
|
throw new BadDataException(`Unknown column: ${key}`);
|
|
1175
1182
|
}
|
|
1176
1183
|
|
|
1184
|
+
if (first) {
|
|
1185
|
+
first = false;
|
|
1186
|
+
} else {
|
|
1187
|
+
sortStatement.append(SQL`, `);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1177
1190
|
const value: SortOrder = sort[key]!;
|
|
1178
1191
|
sortStatement.append(SQL`${key} `).append(
|
|
1179
1192
|
{
|
|
@@ -16,6 +16,8 @@ import { JSONArray, JSONObject } from "../../../../Types/JSON";
|
|
|
16
16
|
import API from "../../../../Utils/API";
|
|
17
17
|
import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
18
18
|
import {
|
|
19
|
+
GitHubAppClientId,
|
|
20
|
+
GitHubAppClientSecret,
|
|
19
21
|
GitHubAppId,
|
|
20
22
|
GitHubAppPrivateKey,
|
|
21
23
|
GitHubAppWebhookSecret,
|
|
@@ -686,6 +688,140 @@ export default class GitHubUtil extends HostedCodeRepository {
|
|
|
686
688
|
return GitHubUtil.summarizeCheckRuns(allCheckRuns);
|
|
687
689
|
}
|
|
688
690
|
|
|
691
|
+
/*
|
|
692
|
+
* Exchanges the short-lived OAuth `code` GitHub appends to the install
|
|
693
|
+
* redirect for a user-to-server access token.
|
|
694
|
+
*
|
|
695
|
+
* This is the ONLY thing that ties a redirect back to the human who
|
|
696
|
+
* actually performed the install: `installation_id` is just a number in a
|
|
697
|
+
* query string that anyone can type, but `code` is minted by GitHub for one
|
|
698
|
+
* specific GitHub user and is single-use. Pair it with
|
|
699
|
+
* listInstallationIdsForUserAccessToken below to answer "does this person
|
|
700
|
+
* really control this installation?".
|
|
701
|
+
*/
|
|
702
|
+
@CaptureSpan()
|
|
703
|
+
public static async exchangeOAuthCodeForUserAccessToken(
|
|
704
|
+
code: string,
|
|
705
|
+
): Promise<string> {
|
|
706
|
+
if (!GitHubAppClientId || !GitHubAppClientSecret) {
|
|
707
|
+
throw new BadDataException(
|
|
708
|
+
'GitHub App OAuth is not configured on this OneUptime instance. Set GITHUB_APP_CLIENT_ID and GITHUB_APP_CLIENT_SECRET, and enable "Request user authorization (OAuth) during installation" in the GitHub App settings.',
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const result: HTTPErrorResponse | HTTPResponse<JSONObject> = await API.post(
|
|
713
|
+
{
|
|
714
|
+
url: URL.fromString("https://github.com/login/oauth/access_token"),
|
|
715
|
+
data: {
|
|
716
|
+
client_id: GitHubAppClientId,
|
|
717
|
+
client_secret: GitHubAppClientSecret,
|
|
718
|
+
code: code,
|
|
719
|
+
},
|
|
720
|
+
headers: {
|
|
721
|
+
Accept: "application/json",
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
);
|
|
725
|
+
|
|
726
|
+
if (result instanceof HTTPErrorResponse) {
|
|
727
|
+
throw new BadDataException(
|
|
728
|
+
"Could not verify the GitHub App installation with GitHub. Please restart the installation from OneUptime.",
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const accessToken: string | undefined =
|
|
733
|
+
result.data["access_token"]?.toString();
|
|
734
|
+
|
|
735
|
+
/*
|
|
736
|
+
* GitHub answers a bad / expired / already-redeemed code with HTTP 200 and
|
|
737
|
+
* an `error` body, so a non-error status is not enough on its own.
|
|
738
|
+
*/
|
|
739
|
+
if (!accessToken) {
|
|
740
|
+
throw new BadDataException(
|
|
741
|
+
"The GitHub authorization code is invalid or has already been used. Please restart the installation from OneUptime.",
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
return accessToken;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/*
|
|
749
|
+
* Lists the IDs of every installation of THIS GitHub App that the user
|
|
750
|
+
* behind `userAccessToken` can administer. GitHub scopes this endpoint to
|
|
751
|
+
* the app the token was issued for, so a token minted by another app cannot
|
|
752
|
+
* widen it.
|
|
753
|
+
*/
|
|
754
|
+
@CaptureSpan()
|
|
755
|
+
public static async listInstallationIdsForUserAccessToken(
|
|
756
|
+
userAccessToken: string,
|
|
757
|
+
): Promise<Array<string>> {
|
|
758
|
+
const installationIds: Array<string> = [];
|
|
759
|
+
let page: number = 1;
|
|
760
|
+
let hasMore: boolean = true;
|
|
761
|
+
|
|
762
|
+
while (hasMore) {
|
|
763
|
+
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
764
|
+
await API.get({
|
|
765
|
+
url: URL.fromString(
|
|
766
|
+
`https://api.github.com/user/installations?per_page=100&page=${page}`,
|
|
767
|
+
),
|
|
768
|
+
headers: {
|
|
769
|
+
Authorization: `Bearer ${userAccessToken}`,
|
|
770
|
+
Accept: "application/vnd.github+json",
|
|
771
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
772
|
+
},
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
if (result instanceof HTTPErrorResponse) {
|
|
776
|
+
throw new BadDataException(
|
|
777
|
+
"Could not read your GitHub App installations from GitHub. Please restart the installation from OneUptime.",
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const installations: JSONArray =
|
|
782
|
+
(result.data["installations"] as JSONArray) || [];
|
|
783
|
+
|
|
784
|
+
for (const installation of installations) {
|
|
785
|
+
const installationId: string | undefined = (
|
|
786
|
+
installation as JSONObject
|
|
787
|
+
)?.["id"]?.toString();
|
|
788
|
+
|
|
789
|
+
if (installationId) {
|
|
790
|
+
installationIds.push(installationId);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
hasMore = installations.length === 100;
|
|
795
|
+
page++;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return installationIds;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/*
|
|
802
|
+
* Proves that the person completing the install redirect actually controls
|
|
803
|
+
* the installation they claim, by round-tripping the OAuth code through
|
|
804
|
+
* GitHub. Throws (never returns false) so no caller can accidentally treat
|
|
805
|
+
* a verification failure as a pass.
|
|
806
|
+
*/
|
|
807
|
+
@CaptureSpan()
|
|
808
|
+
public static async assertUserControlsInstallation(data: {
|
|
809
|
+
oauthCode: string;
|
|
810
|
+
installationId: string;
|
|
811
|
+
}): Promise<void> {
|
|
812
|
+
const userAccessToken: string =
|
|
813
|
+
await GitHubUtil.exchangeOAuthCodeForUserAccessToken(data.oauthCode);
|
|
814
|
+
|
|
815
|
+
const installationIds: Array<string> =
|
|
816
|
+
await GitHubUtil.listInstallationIdsForUserAccessToken(userAccessToken);
|
|
817
|
+
|
|
818
|
+
if (!installationIds.includes(data.installationId.toString())) {
|
|
819
|
+
throw new BadDataException(
|
|
820
|
+
"The GitHub account you authorized does not have access to this GitHub App installation. Please restart the installation from OneUptime.",
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
689
825
|
/**
|
|
690
826
|
* Lists repositories accessible to a GitHub App installation
|
|
691
827
|
* @param installationId - The GitHub App installation ID
|
|
@@ -767,7 +903,15 @@ export default class GitHubUtil extends HostedCodeRepository {
|
|
|
767
903
|
branchName: string;
|
|
768
904
|
}): Promise<Array<string>> {
|
|
769
905
|
const cacheNamespace: string = "github-repo-tree";
|
|
770
|
-
|
|
906
|
+
|
|
907
|
+
/*
|
|
908
|
+
* The installation is part of the key because this cache is read BEFORE a
|
|
909
|
+
* token is minted — so the key is doing the access control for a cache
|
|
910
|
+
* hit. Keyed on org/repo/branch alone, any caller who could name someone
|
|
911
|
+
* else's repository would be served that repository's cached private file
|
|
912
|
+
* listing without ever proving access to it.
|
|
913
|
+
*/
|
|
914
|
+
const cacheKey: string = `${data.installationId}:${data.organizationName}/${data.repositoryName}@${data.branchName}`;
|
|
771
915
|
|
|
772
916
|
try {
|
|
773
917
|
const cachedPaths: Array<string> | null =
|
|
@@ -1415,11 +1559,20 @@ export default class GitHubUtil extends HostedCodeRepository {
|
|
|
1415
1559
|
payload: string,
|
|
1416
1560
|
signature: string,
|
|
1417
1561
|
): boolean {
|
|
1562
|
+
/*
|
|
1563
|
+
* Fail closed. This used to return true when the secret was unset, which
|
|
1564
|
+
* made the webhook endpoint fully unauthenticated on any instance that had
|
|
1565
|
+
* not configured one — and the handler acts on what it is sent: the
|
|
1566
|
+
* `installation.deleted` branch clears installation IDs across every
|
|
1567
|
+
* tenant, and `installation_repositories` triggers repository imports. An
|
|
1568
|
+
* unconfigured secret is a misconfiguration to surface, not a check to
|
|
1569
|
+
* skip.
|
|
1570
|
+
*/
|
|
1418
1571
|
if (!GitHubAppWebhookSecret) {
|
|
1419
|
-
logger.
|
|
1420
|
-
"GITHUB_APP_WEBHOOK_SECRET is not set,
|
|
1572
|
+
logger.error(
|
|
1573
|
+
"Rejecting GitHub webhook: GITHUB_APP_WEBHOOK_SECRET is not set, so the payload's signature cannot be verified. Set it to the webhook secret configured in the GitHub App settings.",
|
|
1421
1574
|
);
|
|
1422
|
-
return
|
|
1575
|
+
return false;
|
|
1423
1576
|
}
|
|
1424
1577
|
|
|
1425
1578
|
const expectedSignature: string = `sha256=${crypto
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import ProjectService from "../../../Services/ProjectService";
|
|
2
|
+
import Project from "../../../../Models/DatabaseModels/Project";
|
|
3
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
4
|
+
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
5
|
+
import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* A GitHub App installation ID is a small, guessable integer, and the single
|
|
9
|
+
* GitHub App JWT this instance holds will mint a `ghs_` token for ANY of them
|
|
10
|
+
* (GitHubUtil.getInstallationAccessToken does not — and cannot — know which
|
|
11
|
+
* tenant asked). So the installation ID alone is not a capability: on its own
|
|
12
|
+
* it is just an attacker-supplied number.
|
|
13
|
+
*
|
|
14
|
+
* Project.gitHubAppInstallationId is the ONE authoritative record of which
|
|
15
|
+
* project an installation belongs to. It is written only by the GitHub App
|
|
16
|
+
* install callback, and only after GitHub itself has confirmed — via the
|
|
17
|
+
* OAuth user-to-server round trip — that the person completing the install
|
|
18
|
+
* controls that installation. Both `create` and `update` on that column are
|
|
19
|
+
* closed to API callers, so nothing outside that callback can assert a
|
|
20
|
+
* binding.
|
|
21
|
+
*
|
|
22
|
+
* Every path that turns an installation ID into GitHub access must therefore
|
|
23
|
+
* re-derive the binding from here rather than trusting the ID it was handed.
|
|
24
|
+
* Checking that a CodeRepository row is in the caller's project is NOT enough
|
|
25
|
+
* on its own: the attacker owns their own project, so they control both sides
|
|
26
|
+
* of that comparison.
|
|
27
|
+
*/
|
|
28
|
+
export default class GitHubInstallationBinding {
|
|
29
|
+
/*
|
|
30
|
+
* The installation this project owns, or null if it has none. Callers
|
|
31
|
+
* filtering a whole project's repositories should read it once and compare
|
|
32
|
+
* in memory rather than asking per row — every row in such a list shares
|
|
33
|
+
* the same project, so the answer cannot differ between them.
|
|
34
|
+
*/
|
|
35
|
+
@CaptureSpan()
|
|
36
|
+
public static async getBoundInstallationId(
|
|
37
|
+
projectId: ObjectID,
|
|
38
|
+
): Promise<string | null> {
|
|
39
|
+
const project: Project | null = await ProjectService.findOneBy({
|
|
40
|
+
query: {
|
|
41
|
+
_id: projectId.toString(),
|
|
42
|
+
},
|
|
43
|
+
select: {
|
|
44
|
+
_id: true,
|
|
45
|
+
gitHubAppInstallationId: true,
|
|
46
|
+
},
|
|
47
|
+
props: {
|
|
48
|
+
isRoot: true,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return project?.gitHubAppInstallationId || null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// True only if `installationId` is the installation bound to `projectId`.
|
|
56
|
+
@CaptureSpan()
|
|
57
|
+
public static async isInstallationBoundToProject(data: {
|
|
58
|
+
projectId: ObjectID;
|
|
59
|
+
installationId: string;
|
|
60
|
+
}): Promise<boolean> {
|
|
61
|
+
if (!data.installationId) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const project: Project | null = await ProjectService.findOneBy({
|
|
66
|
+
query: {
|
|
67
|
+
_id: data.projectId.toString(),
|
|
68
|
+
gitHubAppInstallationId: data.installationId.toString(),
|
|
69
|
+
},
|
|
70
|
+
select: {
|
|
71
|
+
_id: true,
|
|
72
|
+
},
|
|
73
|
+
props: {
|
|
74
|
+
isRoot: true,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return Boolean(project);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/*
|
|
82
|
+
* Convenience for the many callers that hold a CodeRepository row and are
|
|
83
|
+
* about to use its installation ID against GitHub. Returns false for a row
|
|
84
|
+
* that carries no project or no installation, so a half-populated row can
|
|
85
|
+
* never be mistaken for a permitted one.
|
|
86
|
+
*
|
|
87
|
+
* Row-level checks like this stay necessary even with the write boundary
|
|
88
|
+
* closed, because a database that was exploited before the fix can still
|
|
89
|
+
* hold rows whose installation ID never belonged to their project.
|
|
90
|
+
*/
|
|
91
|
+
@CaptureSpan()
|
|
92
|
+
public static async isRepositoryInstallationBound(repository: {
|
|
93
|
+
projectId?: ObjectID | undefined;
|
|
94
|
+
gitHubAppInstallationId?: string | undefined;
|
|
95
|
+
}): Promise<boolean> {
|
|
96
|
+
if (!repository.projectId || !repository.gitHubAppInstallationId) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return GitHubInstallationBinding.isInstallationBoundToProject({
|
|
101
|
+
projectId: repository.projectId,
|
|
102
|
+
installationId: repository.gitHubAppInstallationId,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/*
|
|
107
|
+
* Fail-closed variant for the places where continuing without a binding
|
|
108
|
+
* would hand out cross-tenant access.
|
|
109
|
+
*/
|
|
110
|
+
@CaptureSpan()
|
|
111
|
+
public static async assertInstallationBoundToProject(data: {
|
|
112
|
+
projectId: ObjectID;
|
|
113
|
+
installationId: string;
|
|
114
|
+
}): Promise<void> {
|
|
115
|
+
const isBound: boolean =
|
|
116
|
+
await GitHubInstallationBinding.isInstallationBoundToProject(data);
|
|
117
|
+
|
|
118
|
+
if (!isBound) {
|
|
119
|
+
/*
|
|
120
|
+
* Deliberately vague: a probing caller must not be able to tell
|
|
121
|
+
* "this installation belongs to someone else" from "no such
|
|
122
|
+
* installation", or the error itself becomes the enumeration oracle
|
|
123
|
+
* this check exists to close.
|
|
124
|
+
*/
|
|
125
|
+
throw new BadDataException(
|
|
126
|
+
"This GitHub App installation is not connected to this project. Please connect the repository from Project Settings using the GitHub App.",
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|