@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.
Files changed (384) hide show
  1. package/Models/DatabaseModels/CephCluster.ts +14 -0
  2. package/Models/DatabaseModels/CodeRepository.ts +14 -6
  3. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
  4. package/Models/DatabaseModels/DockerHost.ts +14 -0
  5. package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
  6. package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
  7. package/Models/DatabaseModels/Index.ts +2 -0
  8. package/Models/DatabaseModels/IoTFleet.ts +14 -0
  9. package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
  10. package/Models/DatabaseModels/LogDropFilter.ts +9 -20
  11. package/Models/DatabaseModels/NetworkDevice.ts +14 -0
  12. package/Models/DatabaseModels/NetworkInterface.ts +18 -0
  13. package/Models/DatabaseModels/Project.ts +16 -2
  14. package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
  15. package/Models/DatabaseModels/Service.ts +30 -0
  16. package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
  17. package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
  18. package/Models/DatabaseModels/StatusPageResource.ts +73 -0
  19. package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
  20. package/Models/DatabaseModels/User.ts +30 -0
  21. package/Server/API/AIAgentDataAPI.ts +31 -0
  22. package/Server/API/BaseAPI.ts +13 -2
  23. package/Server/API/DashboardAPI.ts +458 -12
  24. package/Server/API/GitHubAPI.ts +119 -283
  25. package/Server/API/UserAPI.ts +263 -1
  26. package/Server/EnvironmentConfig.ts +38 -4
  27. package/Server/Infrastructure/GlobalCache.ts +152 -0
  28. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  36. package/Server/Services/AlertStateTimelineService.ts +20 -0
  37. package/Server/Services/AnalyticsDatabaseService.ts +72 -1
  38. package/Server/Services/CephClusterService.ts +26 -86
  39. package/Server/Services/CloudResourceService.ts +23 -83
  40. package/Server/Services/CodeRepositoryService.ts +105 -2
  41. package/Server/Services/DatabaseService.ts +296 -2
  42. package/Server/Services/DockerHostService.ts +19 -79
  43. package/Server/Services/DockerSwarmClusterService.ts +27 -88
  44. package/Server/Services/HostService.ts +46 -99
  45. package/Server/Services/IncidentStateTimelineService.ts +20 -0
  46. package/Server/Services/Index.ts +2 -0
  47. package/Server/Services/IoTFleetService.ts +19 -79
  48. package/Server/Services/KubernetesClusterService.ts +17 -78
  49. package/Server/Services/LabelService.ts +57 -10
  50. package/Server/Services/MetricTypeService.ts +84 -0
  51. package/Server/Services/MonitorService.ts +89 -14
  52. package/Server/Services/MonitorStatusTimelineService.ts +29 -0
  53. package/Server/Services/MonitorTemplateService.ts +65 -0
  54. package/Server/Services/OpenTelemetryIngestService.ts +230 -26
  55. package/Server/Services/PodmanHostService.ts +19 -79
  56. package/Server/Services/ProxmoxClusterService.ts +23 -83
  57. package/Server/Services/RumApplicationService.ts +19 -80
  58. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
  59. package/Server/Services/ServerlessFunctionService.ts +24 -85
  60. package/Server/Services/ServiceService.ts +299 -73
  61. package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
  62. package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
  63. package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
  64. package/Server/Services/TelemetryEntityService.ts +1 -0
  65. package/Server/Services/UserService.ts +184 -0
  66. package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
  67. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
  68. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
  69. package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
  70. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
  71. package/Server/Utils/Monitor/MonitorResource.ts +63 -36
  72. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
  73. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
  74. package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
  75. package/Server/Utils/PasswordHash.ts +306 -0
  76. package/Server/Utils/SingleFlight.ts +83 -0
  77. package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
  78. package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
  79. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
  80. package/Server/Utils/Telemetry/Telemetry.ts +351 -122
  81. package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
  82. package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
  83. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  84. package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
  85. package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
  86. package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
  87. package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
  88. package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
  89. package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
  90. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
  91. package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
  92. package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
  93. package/Tests/Server/API/Helpers.ts +6 -1
  94. package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
  95. package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
  96. package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
  97. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
  98. package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
  99. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
  100. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
  101. package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
  102. package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
  103. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
  104. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
  105. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
  106. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
  107. package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
  108. package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
  109. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
  110. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
  111. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
  112. package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
  113. package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
  114. package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
  115. package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
  116. package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
  117. package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
  118. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
  119. package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
  120. package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
  121. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
  122. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
  123. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
  124. package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
  125. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
  126. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
  127. package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
  128. package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
  129. package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
  130. package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
  131. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
  132. package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
  133. package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
  134. package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
  135. package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
  136. package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
  137. package/Tests/Types/Database/ColumnLength.test.ts +7 -1
  138. package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
  139. package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
  140. package/Tests/Types/Decimal.test.ts +37 -0
  141. package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
  142. package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
  143. package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
  144. package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
  145. package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
  146. package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
  147. package/Tests/Types/Port.test.ts +38 -0
  148. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
  149. package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
  150. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
  151. package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
  152. package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
  153. package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
  154. package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
  155. package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
  156. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  157. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  158. package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
  159. package/Types/Dashboard/DashboardTemplates.ts +397 -0
  160. package/Types/Database/BigIntColumnTransformer.ts +74 -0
  161. package/Types/Database/ColumnLength.ts +9 -1
  162. package/Types/Database/DatabaseProperty.ts +26 -0
  163. package/Types/Database/NumericColumnValue.ts +128 -0
  164. package/Types/Database/TableColumn.ts +11 -0
  165. package/Types/Database/UnsynchronizedIndex.ts +48 -0
  166. package/Types/Decimal.ts +16 -4
  167. package/Types/HashedString.ts +139 -4
  168. package/Types/Permission.ts +48 -0
  169. package/Types/Port.ts +16 -4
  170. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
  171. package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
  172. package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
  173. package/UI/Components/Navbar/NavBar.tsx +12 -2
  174. package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
  175. package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
  176. package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
  177. package/Utils/Dashboard/Components/Index.ts +7 -0
  178. package/Utils/TeamMembersByProject.ts +237 -0
  179. package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
  180. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  181. package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
  182. package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
  184. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
  186. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  187. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
  188. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
  189. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
  190. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
  191. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  192. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  193. package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
  194. package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
  195. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
  196. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  197. package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
  198. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  199. package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
  200. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  201. package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
  202. package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
  203. package/build/dist/Models/DatabaseModels/Project.js +16 -2
  204. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  205. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
  206. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  207. package/build/dist/Models/DatabaseModels/Service.js +28 -1
  208. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  209. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
  210. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
  211. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
  212. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
  213. package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
  214. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  215. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
  216. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/User.js +32 -0
  218. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/BaseAPI.js +11 -1
  222. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +343 -10
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/GitHubAPI.js +94 -167
  226. package/build/dist/Server/API/GitHubAPI.js.map +1 -1
  227. package/build/dist/Server/API/UserAPI.js +187 -2
  228. package/build/dist/Server/API/UserAPI.js.map +1 -1
  229. package/build/dist/Server/EnvironmentConfig.js +34 -4
  230. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
  232. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
  234. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
  245. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
  246. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
  247. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  248. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  249. package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
  250. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/CephClusterService.js +26 -76
  254. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  255. package/build/dist/Server/Services/CloudResourceService.js +23 -73
  256. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  257. package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
  258. package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
  259. package/build/dist/Server/Services/DatabaseService.js +246 -2
  260. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  261. package/build/dist/Server/Services/DockerHostService.js +19 -69
  262. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  263. package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
  264. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  265. package/build/dist/Server/Services/HostService.js +58 -102
  266. package/build/dist/Server/Services/HostService.js.map +1 -1
  267. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  268. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  269. package/build/dist/Server/Services/Index.js +2 -0
  270. package/build/dist/Server/Services/Index.js.map +1 -1
  271. package/build/dist/Server/Services/IoTFleetService.js +19 -69
  272. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  273. package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
  274. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  275. package/build/dist/Server/Services/LabelService.js +42 -7
  276. package/build/dist/Server/Services/LabelService.js.map +1 -1
  277. package/build/dist/Server/Services/MetricTypeService.js +74 -0
  278. package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
  279. package/build/dist/Server/Services/MonitorService.js +81 -9
  280. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  281. package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
  282. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  283. package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
  284. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  285. package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
  286. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  287. package/build/dist/Server/Services/PodmanHostService.js +19 -69
  288. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  289. package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
  290. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  291. package/build/dist/Server/Services/RumApplicationService.js +19 -70
  292. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  293. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
  294. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  295. package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
  296. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  297. package/build/dist/Server/Services/ServiceService.js +236 -61
  298. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  299. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
  300. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
  301. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
  302. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
  303. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
  304. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  305. package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
  306. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  307. package/build/dist/Server/Services/UserService.js +165 -0
  308. package/build/dist/Server/Services/UserService.js.map +1 -1
  309. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
  310. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
  311. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
  312. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  313. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
  314. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  315. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
  316. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
  317. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
  318. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  319. package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
  320. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  321. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
  322. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  323. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
  324. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
  325. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
  326. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
  327. package/build/dist/Server/Utils/PasswordHash.js +237 -0
  328. package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
  329. package/build/dist/Server/Utils/SingleFlight.js +76 -0
  330. package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
  331. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
  332. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
  333. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
  334. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  335. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
  336. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
  337. package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
  338. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  339. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  340. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  341. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  342. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  343. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
  344. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
  345. package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
  346. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  347. package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
  348. package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
  349. package/build/dist/Types/Database/ColumnLength.js +9 -1
  350. package/build/dist/Types/Database/ColumnLength.js.map +1 -1
  351. package/build/dist/Types/Database/DatabaseProperty.js +25 -0
  352. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  353. package/build/dist/Types/Database/NumericColumnValue.js +97 -0
  354. package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
  355. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  356. package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
  357. package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
  358. package/build/dist/Types/Decimal.js +14 -3
  359. package/build/dist/Types/Decimal.js.map +1 -1
  360. package/build/dist/Types/HashedString.js +95 -5
  361. package/build/dist/Types/HashedString.js.map +1 -1
  362. package/build/dist/Types/Permission.js +42 -0
  363. package/build/dist/Types/Permission.js.map +1 -1
  364. package/build/dist/Types/Port.js +15 -5
  365. package/build/dist/Types/Port.js.map +1 -1
  366. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
  367. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  368. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
  369. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
  370. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
  371. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
  372. package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
  373. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  374. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
  375. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  376. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
  377. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
  378. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
  379. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
  380. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  381. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  382. package/build/dist/Utils/TeamMembersByProject.js +145 -0
  383. package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
  384. 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
- return repositories.filter((repository: CodeRepository) => {
138
- return (
139
- repository.repositoryHostedAt === CodeRepositoryType.GitHub &&
140
- Boolean(repository.gitHubAppInstallationId)
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
- const cacheKey: string = `${data.organizationName}/${data.repositoryName}@${data.branchName}`;
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.warn(
1420
- "GITHUB_APP_WEBHOOK_SECRET is not set, skipping verification",
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 true;
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
+ }