@oneuptime/common 13.0.0 → 13.0.1

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 (734) hide show
  1. package/Models/DatabaseModels/Alert.ts +55 -0
  2. package/Models/DatabaseModels/CloudResource.ts +45 -7
  3. package/Models/DatabaseModels/CodeRepository.ts +85 -0
  4. package/Models/DatabaseModels/GoogleSecOpsConnection.ts +57 -7
  5. package/Models/DatabaseModels/GoogleSecOpsConnectionRun.ts +263 -0
  6. package/Models/DatabaseModels/Incident.ts +55 -0
  7. package/Models/DatabaseModels/Index.ts +16 -0
  8. package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
  9. package/Models/DatabaseModels/User.ts +51 -2
  10. package/Models/DatabaseModels/UserNotificationEmailRollupItem.ts +43 -2
  11. package/Models/DatabaseModels/UserWebAuthn.ts +17 -0
  12. package/Models/DatabaseModels/VMwareResource.ts +1149 -0
  13. package/Models/DatabaseModels/VMwareVCenter.ts +1122 -0
  14. package/Models/DatabaseModels/VMwareVCenterFeed.ts +625 -0
  15. package/Models/DatabaseModels/VMwareVCenterLabelRule.ts +514 -0
  16. package/Models/DatabaseModels/VMwareVCenterOwnerRule.ts +596 -0
  17. package/Models/DatabaseModels/VMwareVCenterOwnerTeam.ts +485 -0
  18. package/Models/DatabaseModels/VMwareVCenterOwnerUser.ts +484 -0
  19. package/Models/DatabaseModels/WorkflowVariable.ts +22 -1
  20. package/Server/API/AIAgentDataAPI.ts +79 -4
  21. package/Server/API/AIAgentGitHubAPI.ts +523 -0
  22. package/Server/API/AIChatAPI.ts +20 -1
  23. package/Server/API/BillingAPI.ts +33 -0
  24. package/Server/API/DashboardAPI.ts +10 -0
  25. package/Server/API/GitHubAPI.ts +47 -5
  26. package/Server/API/GoogleSecOpsConnectionAPI.ts +79 -0
  27. package/Server/API/UserWebAuthnAPI.ts +21 -1
  28. package/Server/API/VMwareResourceAPI.ts +143 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1791800000000-SplitWebAuthnChallengeByPurpose.ts +68 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1791900000000-AddSeverityAndStateToNotificationEmailRollup.ts +26 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1792000000000-AddVMwareTables.ts +636 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1792100000000-AddGitHubCommandSettingsToCodeRepository.ts +47 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1792200000000-AddUserWebAuthnPurpose.ts +17 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1792300000000-AddGoogleSecOpsDiagnostics.ts +83 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  36. package/Server/Middleware/IdentityRateLimit.ts +101 -0
  37. package/Server/Services/AIRunService.ts +13 -0
  38. package/Server/Services/BillingPaymentMethodService.ts +99 -28
  39. package/Server/Services/BillingService.ts +287 -20
  40. package/Server/Services/CloudResourceInstanceService.ts +70 -0
  41. package/Server/Services/ExceptionAggregationService.ts +1 -0
  42. package/Server/Services/GoogleSecOpsConnectionRunService.ts +10 -0
  43. package/Server/Services/GoogleSecOpsConnectionService.ts +12 -0
  44. package/Server/Services/Index.ts +16 -0
  45. package/Server/Services/LogAggregationService.ts +1 -0
  46. package/Server/Services/MetricAggregationService.ts +1 -0
  47. package/Server/Services/MobilePasskeyLoginService.ts +216 -0
  48. package/Server/Services/MonitorService.ts +51 -1
  49. package/Server/Services/MonitorTemplateService.ts +91 -29
  50. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +132 -1
  51. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +176 -2
  52. package/Server/Services/OpenTelemetryIngestService.ts +17 -0
  53. package/Server/Services/PayAsYouGoBillingService.ts +446 -0
  54. package/Server/Services/ProjectService.ts +46 -2
  55. package/Server/Services/ServerlessFunctionInstanceService.ts +71 -0
  56. package/Server/Services/TeamMemberService.ts +96 -36
  57. package/Server/Services/TelemetryIngestionKeyService.ts +80 -10
  58. package/Server/Services/TelemetryUsageBillingService.ts +95 -5
  59. package/Server/Services/TraceAggregationService.ts +5 -4
  60. package/Server/Services/UserWebAuthnService.ts +592 -133
  61. package/Server/Services/VMwareResourceService.ts +648 -0
  62. package/Server/Services/VMwareVCenterFeedService.ts +99 -0
  63. package/Server/Services/VMwareVCenterLabelRuleEngineService.ts +240 -0
  64. package/Server/Services/VMwareVCenterLabelRuleService.ts +14 -0
  65. package/Server/Services/VMwareVCenterOwnerRuleEngineService.ts +252 -0
  66. package/Server/Services/VMwareVCenterOwnerRuleService.ts +14 -0
  67. package/Server/Services/VMwareVCenterOwnerTeamService.ts +139 -0
  68. package/Server/Services/VMwareVCenterOwnerUserService.ts +133 -0
  69. package/Server/Services/VMwareVCenterService.ts +629 -0
  70. package/Server/Services/WorkflowVariableService.ts +203 -0
  71. package/Server/Types/Billing/MeteredPlan/ActiveMonitoringMeteredPlan.ts +17 -0
  72. package/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.ts +31 -1
  73. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +7 -0
  74. package/Server/Utils/CodeRepository/GitHub/GitHubAgentTaskTrigger.ts +341 -0
  75. package/Server/Utils/CodeRepository/GitHub/GitHubCommandAuthorizer.ts +274 -0
  76. package/Server/Utils/CodeRepository/GitHub/GitHubCommandParser.ts +375 -0
  77. package/Server/Utils/CodeRepository/GitHub/GitHubConversation.ts +846 -0
  78. package/Server/Utils/CodeRepository/GitHub/GitHubReplyComposer.ts +360 -0
  79. package/Server/Utils/CodeRepository/GitHub/GitHubRunReply.ts +421 -0
  80. package/Server/Utils/CodeRepository/GitHub/GitHubWebhookEvents.ts +140 -0
  81. package/Server/Utils/CodeRepository/GitHub/GitHubWebhookHandler.ts +906 -0
  82. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +143 -0
  83. package/Server/Utils/DataSource/EgressGuard.ts +160 -15
  84. package/Server/Utils/Database/TruncateColumnValue.ts +1 -1
  85. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +2 -0
  86. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +33 -0
  87. package/Server/Utils/EmailRollup/EmailRollupWriter.ts +44 -0
  88. package/Server/Utils/LogRedaction.ts +2 -1
  89. package/Server/Utils/Monitor/MonitorAlert.ts +1 -1
  90. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +302 -3
  91. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +26 -4
  92. package/Server/Utils/Monitor/MonitorResourceContext.ts +5 -3
  93. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +14 -0
  94. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
  95. package/Server/Utils/Monitor/SeriesResourceLabels.ts +26 -0
  96. package/Server/Utils/Monitor/SeriesResourceLinker.ts +28 -5
  97. package/Server/Utils/ResourceFeed/ResourceFeedUtil.ts +3 -3
  98. package/Server/Utils/SecurityEvent/ConnectorErrorMessage.ts +14 -9
  99. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +116 -84
  100. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +594 -195
  101. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +466 -0
  102. package/Server/Utils/Telemetry/EntityRegistry.ts +5 -0
  103. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +2 -1
  104. package/Server/Utils/Telemetry/TelemetryEntity.ts +181 -1
  105. package/Server/Utils/Telemetry/VMwareSnapshotScan.ts +1352 -0
  106. package/Tests/App/Accounts/LoginPasskey.test.tsx +529 -0
  107. package/Tests/App/Accounts/MobilePasskey.test.tsx +620 -0
  108. package/Tests/App/Accounts/PasswordRequirements.test.tsx +254 -0
  109. package/Tests/App/Accounts/RegisterPassword.test.tsx +553 -0
  110. package/Tests/App/Dashboard/AskAiAvailability.test.ts +224 -0
  111. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +432 -0
  112. package/Tests/App/Dashboard/BackupCodes.test.tsx +414 -0
  113. package/Tests/App/Dashboard/BillingPaidUsageFlow.test.tsx +530 -0
  114. package/Tests/App/Dashboard/BillingPaymentMethodForm.test.tsx +195 -0
  115. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +199 -0
  116. package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +198 -0
  117. package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +215 -0
  118. package/Tests/App/Dashboard/CloudFleetSummary.test.ts +183 -0
  119. package/Tests/App/Dashboard/CloudFleetSummaryComponent.test.tsx +248 -0
  120. package/Tests/App/Dashboard/CloudResourceTelemetryScope.test.ts +215 -0
  121. package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +3 -0
  122. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +441 -0
  123. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +458 -0
  124. package/Tests/App/Dashboard/DashboardToolbarPopupLayering.test.tsx +566 -0
  125. package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +376 -0
  126. package/Tests/App/Dashboard/DiscoveryScanLiveUpdates.test.tsx +642 -0
  127. package/Tests/App/Dashboard/DiscoveryScanProgress.test.tsx +267 -0
  128. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +69 -231
  129. package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +244 -0
  130. package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +724 -0
  131. package/Tests/App/Dashboard/MonitorTemplateSyncFields.test.tsx +539 -0
  132. package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +24 -7
  133. package/Tests/App/Dashboard/OnCallOverridePresentation.test.ts +496 -0
  134. package/Tests/App/Dashboard/OnCallScheduleOverrideClarity.test.tsx +677 -0
  135. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +31 -3
  136. package/Tests/App/Dashboard/PaidUsageConsent.test.tsx +180 -0
  137. package/Tests/App/Dashboard/PasskeySettings.test.tsx +1220 -0
  138. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +176 -275
  139. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +380 -233
  140. package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +3 -23
  141. package/Tests/App/Dashboard/ResourceFeed.test.tsx +1 -0
  142. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
  143. package/Tests/App/Dashboard/TwoFactorStatus.test.tsx +260 -0
  144. package/Tests/App/Dashboard/VMwareDashboardWidgets.test.ts +573 -0
  145. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  146. package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +768 -0
  147. package/Tests/App/StatusPage/StatusPageOverviewLiveAndSearch.test.tsx +532 -40
  148. package/Tests/App/StatusPage/StatusPageResourceSearchBox.test.tsx +19 -2
  149. package/Tests/Fixtures/MobilePasskeyUser.json +8 -0
  150. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +25 -6
  151. package/Tests/Models/DatabaseModels/VMwareModels.test.ts +1134 -0
  152. package/Tests/Models/RunnerVersionLabel.test.ts +4 -2
  153. package/Tests/Server/API/AIChatProvidersAIEnabled.test.ts +259 -0
  154. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +31 -2
  155. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +116 -0
  156. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +29 -1
  157. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +13 -2
  158. package/Tests/Server/API/GitHubWebhookSignature.test.ts +368 -0
  159. package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +248 -0
  160. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +330 -0
  161. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +172 -0
  162. package/Tests/Server/API/VMwareResourceAPI.test.ts +546 -0
  163. package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupMetadataMigration.test.ts +85 -0
  164. package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupTableMigration.test.ts +34 -3
  165. package/Tests/Server/Middleware/PasskeyRateLimit.test.ts +141 -0
  166. package/Tests/Server/Services/AutoImportRuleRearmRecentScans.test.ts +343 -0
  167. package/Tests/Server/Services/AutoImportRuleWriteRearmsScans.test.ts +381 -0
  168. package/Tests/Server/Services/BillingPaymentMethodServiceSync.test.ts +175 -0
  169. package/Tests/Server/Services/BillingService.test.ts +26 -11
  170. package/Tests/Server/Services/BillingServiceMeteredPaymentAuthorization.test.ts +245 -0
  171. package/Tests/Server/Services/BillingServicePaymentMethodReads.test.ts +362 -0
  172. package/Tests/Server/Services/CloudResourceInstanceStalePrune.test.ts +188 -0
  173. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +16 -2
  174. package/Tests/Server/Services/GoogleSecOpsDiagnosticsModel.test.ts +153 -0
  175. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +13 -0
  176. package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +5 -0
  177. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +36 -0
  178. package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +151 -0
  179. package/Tests/Server/Services/MobilePasskeyLoginIntegration.test.ts +231 -0
  180. package/Tests/Server/Services/MobilePasskeyLoginService.test.ts +315 -0
  181. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +302 -0
  182. package/Tests/Server/Services/MonitorTemplateServiceFieldSync.test.ts +994 -0
  183. package/Tests/Server/Services/NetworkDeviceAutoImportRuleService.test.ts +22 -0
  184. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
  185. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +857 -0
  186. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +379 -0
  187. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +99 -1
  188. package/Tests/Server/Services/ProjectServiceSubscriptionSeats.test.ts +134 -0
  189. package/Tests/Server/Services/ResourceFeedServices.test.ts +3 -2
  190. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +32 -6
  191. package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +363 -175
  192. package/Tests/Server/Services/ServerlessFunctionInstanceStalePrune.test.ts +200 -0
  193. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +3 -0
  194. package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +762 -0
  195. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +3 -0
  196. package/Tests/Server/Services/TelemetryIngestionKeyPolicyResolution.test.ts +72 -0
  197. package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +17 -4
  198. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +355 -0
  199. package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +41 -4
  200. package/Tests/Server/Services/UserNotificationEmailRollupModels.test.ts +37 -0
  201. package/Tests/Server/Services/UserWebAuthnPasskeyIntegration.test.ts +401 -0
  202. package/Tests/Server/Services/UserWebAuthnPasskeyService.test.ts +769 -0
  203. package/Tests/Server/Services/UserWebAuthnUserVerification.test.ts +649 -0
  204. package/Tests/Server/Services/VMwareResourceService.test.ts +945 -0
  205. package/Tests/Server/Services/VMwareVCenterService.test.ts +722 -0
  206. package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +842 -0
  207. package/Tests/Server/TestingUtils/PasskeyHttpIntegration.ts +102 -0
  208. package/Tests/Server/TestingUtils/SecurityKey.ts +464 -0
  209. package/Tests/Server/TestingUtils/WebAuthnServiceMocks.ts +60 -0
  210. package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +573 -0
  211. package/Tests/Server/Types/Database/Permissions/ProjectCustomerSupportAccessColumn.test.ts +261 -0
  212. package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +95 -0
  213. package/Tests/Server/Utils/CodeRepository/GitHubAgentTaskTrigger.test.ts +2101 -0
  214. package/Tests/Server/Utils/CodeRepository/GitHubCommandAuthorizer.test.ts +883 -0
  215. package/Tests/Server/Utils/CodeRepository/GitHubCommandParser.test.ts +1474 -0
  216. package/Tests/Server/Utils/CodeRepository/GitHubConversation.test.ts +1526 -0
  217. package/Tests/Server/Utils/CodeRepository/GitHubReplyComposer.test.ts +1389 -0
  218. package/Tests/Server/Utils/CodeRepository/GitHubRunReply.test.ts +1707 -0
  219. package/Tests/Server/Utils/CodeRepository/GitHubWebhookHandler.test.ts +2202 -0
  220. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +345 -0
  221. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +896 -0
  222. package/Tests/Server/Utils/EmailRollup/EmailRollupMetadataIntegration.test.ts +246 -0
  223. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +299 -1
  224. package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +6 -0
  225. package/Tests/Server/Utils/EmailRollup/EmailRollupWriter.test.ts +185 -0
  226. package/Tests/Server/Utils/LogRedaction.test.ts +33 -0
  227. package/Tests/Server/Utils/Monitor/CapturedMetricAttributeUtil.test.ts +2 -1
  228. package/Tests/Server/Utils/Monitor/Criteria/TelemetryStepMetricAlias.test.ts +10 -0
  229. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +66 -1
  230. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +362 -0
  231. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
  232. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +60 -1
  233. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +60 -0
  234. package/Tests/Server/Utils/Monitor/MonitorRootCauseTargetResolution.test.ts +423 -0
  235. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +6 -0
  236. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
  237. package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
  238. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +46 -2
  239. package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +81 -45
  240. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +711 -0
  241. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClickhouseIntegration.test.ts +223 -0
  242. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +69 -1
  243. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +838 -0
  244. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +241 -28
  245. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +651 -0
  246. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +11 -0
  247. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +13 -12
  248. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +112 -127
  249. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +12 -18
  250. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +43 -0
  251. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +580 -0
  252. package/Tests/Server/Utils/Telemetry/VMwareSnapshotScan.test.ts +1748 -0
  253. package/Tests/Types/Cloud/CloudPlatform.test.ts +574 -0
  254. package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +2 -0
  255. package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +9 -0
  256. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +1 -0
  257. package/Tests/Types/Monitor/MonitorStepInfraMonitorUtils.test.ts +53 -8
  258. package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +97 -0
  259. package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +1 -0
  260. package/Tests/Types/Monitor/MonitorStepTemplateSyncFields.test.ts +107 -0
  261. package/Tests/Types/Monitor/MonitorTemplateSyncField.test.ts +172 -0
  262. package/Tests/Types/Monitor/MonitorType.test.ts +1 -0
  263. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +2 -0
  264. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationAlertDebuggability.test.ts +1 -0
  265. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +9 -0
  266. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
  267. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
  268. package/Tests/Types/Monitor/SeriesContext/SeriesDebugHints.test.ts +131 -0
  269. package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +10 -0
  270. package/Tests/Types/Monitor/VMwareAlertTemplates.test.ts +1608 -0
  271. package/Tests/Types/Monitor/VMwareMetricCatalog.test.ts +488 -0
  272. package/Tests/Types/OnCallDutyPolicy/OverrideProvenance.test.ts +295 -0
  273. package/Tests/Types/Runner/RunnerLiveStatus.test.ts +2 -2
  274. package/Tests/Types/SignupPassword.test.ts +205 -0
  275. package/Tests/Types/StatusPage/StatusPageLanguage.test.ts +44 -0
  276. package/Tests/Types/SupportedLanguages.test.ts +119 -0
  277. package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +19 -0
  278. package/Tests/UI/ColorPickerBuild.test.ts +86 -0
  279. package/Tests/UI/Components/Breadcrumbs.test.tsx +32 -0
  280. package/Tests/UI/Components/Forms/ModelFormPreservesUserInput.test.tsx +937 -0
  281. package/Tests/UI/Components/Forms/ModelFormWriteOnlyColumnSelect.test.tsx +230 -0
  282. package/Tests/UI/Components/Forms/ValidationFormSteps.test.ts +3 -4
  283. package/Tests/UI/Components/LabelRuleImportExport.test.tsx +907 -0
  284. package/Tests/UI/Components/ModelTable/ModelTableCreateAfterEdit.test.tsx +514 -0
  285. package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +301 -0
  286. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +2 -1
  287. package/Tests/UI/Components/TooltipLazyContent.test.tsx +373 -0
  288. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +43 -12
  289. package/Tests/UI/ReactDomBuild.test.ts +104 -0
  290. package/Tests/UI/ReactRouterSingletonBuild.test.ts +11 -1
  291. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +148 -0
  292. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +29 -0
  293. package/Tests/UI/Utils/DashboardStackingLayers.test.ts +197 -0
  294. package/Tests/UI/Utils/LabelRuleImportExport.test.ts +546 -0
  295. package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +109 -2
  296. package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +7 -2
  297. package/Tests/UI/Utils/UserEmailRegistrationStatus.test.tsx +295 -0
  298. package/Tests/UI/Utils/WebAuthn.test.ts +265 -0
  299. package/Tests/Utils/Dashboard/Components/DashboardComponentDefaults.test.ts +5 -0
  300. package/Tests/Utils/Dashboard/Components/DashboardVMwareListComponents.test.ts +548 -0
  301. package/Tests/Utils/Dashboard/DashboardResourceListSharedArgs.test.ts +17 -3
  302. package/Tests/Utils/LabelRuleImportExport.test.ts +583 -0
  303. package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +1 -0
  304. package/Tests/Utils/Monitor/MonitorTemplateSyncUtil.test.ts +590 -0
  305. package/Tests/Utils/RequestFailedDetails.test.ts +325 -0
  306. package/Tests/Utils/Telemetry/CloudInstanceIdentity.test.ts +255 -0
  307. package/Tests/Utils/Telemetry/EntityRelationship.test.ts +167 -0
  308. package/Tests/Utils/WebAuthnTestUtil.ts +79 -0
  309. package/Tests/__mocks__/simplewebauthn.js +33 -4
  310. package/Types/AI/CodeFixTaskContext.ts +122 -0
  311. package/Types/AI/CodeFixTaskType.ts +62 -0
  312. package/Types/Accounts/AccountsLanguage.ts +1 -0
  313. package/Types/AdminDashboard/AdminDashboardLanguage.ts +1 -0
  314. package/Types/Calendar/CalendarEvent.ts +16 -0
  315. package/Types/Cloud/CloudPlatform.ts +372 -0
  316. package/Types/CodeRepository/GitHubCommand.ts +69 -0
  317. package/Types/Dashboard/DashboardComponentType.ts +2 -0
  318. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  319. package/Types/Dashboard/DashboardComponents/DashboardVMwareHostListComponent.ts +23 -0
  320. package/Types/Dashboard/DashboardComponents/DashboardVMwareVirtualMachineListComponent.ts +30 -0
  321. package/Types/Dashboard/DashboardLanguage.ts +1 -0
  322. package/Types/Dashboard/DashboardTemplates.ts +314 -0
  323. package/Types/Docs/DocsLanguage.ts +1 -0
  324. package/Types/Exception/EgressGuardException.ts +65 -0
  325. package/Types/Icon/IconProp.ts +1 -0
  326. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +36 -0
  327. package/Types/Monitor/MonitorCriteriaInstance.ts +1 -0
  328. package/Types/Monitor/MonitorStep.ts +78 -17
  329. package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +1 -0
  330. package/Types/Monitor/MonitorStepVMwareMonitor.ts +127 -0
  331. package/Types/Monitor/MonitorTemplateSyncField.ts +291 -0
  332. package/Types/Monitor/MonitorType.ts +24 -0
  333. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +33 -0
  334. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +2 -0
  335. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +3 -0
  336. package/Types/Monitor/SeriesContext/SeriesDebugHints.ts +158 -0
  337. package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +61 -0
  338. package/Types/Monitor/VMwareAlertTemplates.ts +1070 -0
  339. package/Types/Monitor/VMwareMetricCatalog.ts +914 -0
  340. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +29 -0
  341. package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +16 -0
  342. package/Types/Password.ts +84 -5
  343. package/Types/Permission.ts +265 -0
  344. package/Types/Probe/RequestFailedDetails.ts +7 -0
  345. package/Types/Runner/RunnerLiveStatus.ts +1 -1
  346. package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -0
  347. package/Types/StatusPage/StatusPageLanguage.ts +1 -0
  348. package/Types/Telemetry/EntityType.ts +21 -0
  349. package/Types/Telemetry/ServiceType.ts +1 -0
  350. package/Typings/Index.d.ts +63 -0
  351. package/UI/Components/Breadcrumbs/Breadcrumbs.tsx +2 -2
  352. package/UI/Components/Calendar/Calendar.css +78 -0
  353. package/UI/Components/Calendar/Calendar.tsx +28 -3
  354. package/UI/Components/Forms/BasicForm.tsx +44 -2
  355. package/UI/Components/Forms/Fields/ColorPicker.tsx +2 -1
  356. package/UI/Components/Forms/Fields/FormField.tsx +16 -3
  357. package/UI/Components/Forms/ModelForm.tsx +159 -5
  358. package/UI/Components/Forms/Types/Field.ts +8 -1
  359. package/UI/Components/Graphs/DayUptimeGraph.tsx +8 -10
  360. package/UI/Components/Icon/Icon.tsx +16 -0
  361. package/UI/Components/Input/Input.tsx +42 -7
  362. package/UI/Components/LabelRule/ImportLabelRulesModal.tsx +441 -0
  363. package/UI/Components/LabelRule/LabelRuleTable.tsx +142 -0
  364. package/UI/Components/ModelTable/BaseModelTable.tsx +22 -5
  365. package/UI/Components/ModelTable/ModelTable.tsx +14 -6
  366. package/UI/Components/MonitorGraphs/Uptime.tsx +17 -27
  367. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +3 -1
  368. package/UI/Components/Tooltip/Tooltip.tsx +52 -6
  369. package/UI/Utils/DashboardStackingLayers.ts +85 -0
  370. package/UI/Utils/LabelRuleImportExport.ts +310 -0
  371. package/UI/Utils/ModelAPI/ModelAPI.ts +4 -3
  372. package/UI/Utils/TelemetryService.ts +4 -3
  373. package/UI/Utils/UseDashboardGridDnd.ts +4 -1
  374. package/UI/Utils/UserEmailRegistrationStatus.ts +33 -5
  375. package/UI/Utils/WebAuthn.ts +220 -0
  376. package/UI/esbuild-config.js +4 -0
  377. package/Utils/API.ts +34 -0
  378. package/Utils/Dashboard/Components/DashboardVMwareHostListComponent.ts +38 -0
  379. package/Utils/Dashboard/Components/DashboardVMwareResourceListShared.ts +70 -0
  380. package/Utils/Dashboard/Components/DashboardVMwareVirtualMachineListComponent.ts +76 -0
  381. package/Utils/Dashboard/Components/Index.ts +16 -0
  382. package/Utils/LabelRuleImportExport.ts +496 -0
  383. package/Utils/Monitor/MonitorTemplateSyncUtil.ts +164 -0
  384. package/Utils/NetworkDiscovery/DiscoveryScanStatus.ts +8 -0
  385. package/Utils/Telemetry/CloudInstanceIdentity.ts +94 -0
  386. package/Utils/Telemetry/EntityKey.ts +19 -0
  387. package/Utils/Telemetry/EntityRelationship.ts +19 -0
  388. package/build/dist/Models/DatabaseModels/Alert.js +54 -0
  389. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  390. package/build/dist/Models/DatabaseModels/CloudResource.js +44 -7
  391. package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
  392. package/build/dist/Models/DatabaseModels/CodeRepository.js +87 -0
  393. package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
  394. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +60 -7
  395. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +1 -1
  396. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnectionRun.js +287 -0
  397. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnectionRun.js.map +1 -0
  398. package/build/dist/Models/DatabaseModels/Incident.js +54 -0
  399. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  400. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  401. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  402. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
  403. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  404. package/build/dist/Models/DatabaseModels/User.js +57 -4
  405. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  406. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js +47 -2
  407. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js.map +1 -1
  408. package/build/dist/Models/DatabaseModels/UserWebAuthn.js +18 -0
  409. package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
  410. package/build/dist/Models/DatabaseModels/VMwareResource.js +1201 -0
  411. package/build/dist/Models/DatabaseModels/VMwareResource.js.map +1 -0
  412. package/build/dist/Models/DatabaseModels/VMwareVCenter.js +1149 -0
  413. package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -0
  414. package/build/dist/Models/DatabaseModels/VMwareVCenterFeed.js +648 -0
  415. package/build/dist/Models/DatabaseModels/VMwareVCenterFeed.js.map +1 -0
  416. package/build/dist/Models/DatabaseModels/VMwareVCenterLabelRule.js +522 -0
  417. package/build/dist/Models/DatabaseModels/VMwareVCenterLabelRule.js.map +1 -0
  418. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerRule.js +603 -0
  419. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerRule.js.map +1 -0
  420. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerTeam.js +503 -0
  421. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerTeam.js.map +1 -0
  422. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerUser.js +502 -0
  423. package/build/dist/Models/DatabaseModels/VMwareVCenterOwnerUser.js.map +1 -0
  424. package/build/dist/Models/DatabaseModels/WorkflowVariable.js +22 -1
  425. package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
  426. package/build/dist/Server/API/AIAgentDataAPI.js +63 -3
  427. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  428. package/build/dist/Server/API/AIAgentGitHubAPI.js +363 -0
  429. package/build/dist/Server/API/AIAgentGitHubAPI.js.map +1 -0
  430. package/build/dist/Server/API/AIChatAPI.js +19 -1
  431. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  432. package/build/dist/Server/API/BillingAPI.js +22 -0
  433. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  434. package/build/dist/Server/API/DashboardAPI.js +10 -0
  435. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  436. package/build/dist/Server/API/GitHubAPI.js +33 -5
  437. package/build/dist/Server/API/GitHubAPI.js.map +1 -1
  438. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +56 -0
  439. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -0
  440. package/build/dist/Server/API/UserWebAuthnAPI.js +17 -1
  441. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  442. package/build/dist/Server/API/VMwareResourceAPI.js +106 -0
  443. package/build/dist/Server/API/VMwareResourceAPI.js.map +1 -0
  444. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791800000000-SplitWebAuthnChallengeByPurpose.js +41 -0
  445. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791800000000-SplitWebAuthnChallengeByPurpose.js.map +1 -0
  446. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791900000000-AddSeverityAndStateToNotificationEmailRollup.js +14 -0
  447. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791900000000-AddSeverityAndStateToNotificationEmailRollup.js.map +1 -0
  448. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792000000000-AddVMwareTables.js +241 -0
  449. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792000000000-AddVMwareTables.js.map +1 -0
  450. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792100000000-AddGitHubCommandSettingsToCodeRepository.js +36 -0
  451. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792100000000-AddGitHubCommandSettingsToCodeRepository.js.map +1 -0
  452. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792200000000-AddUserWebAuthnPurpose.js +12 -0
  453. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792200000000-AddUserWebAuthnPurpose.js.map +1 -0
  454. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792300000000-AddGoogleSecOpsDiagnostics.js +34 -0
  455. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792300000000-AddGoogleSecOpsDiagnostics.js.map +1 -0
  456. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  457. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  458. package/build/dist/Server/Middleware/IdentityRateLimit.js +80 -0
  459. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  460. package/build/dist/Server/Services/AIRunService.js +14 -1
  461. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  462. package/build/dist/Server/Services/BillingPaymentMethodService.js +71 -21
  463. package/build/dist/Server/Services/BillingPaymentMethodService.js.map +1 -1
  464. package/build/dist/Server/Services/BillingService.js +210 -22
  465. package/build/dist/Server/Services/BillingService.js.map +1 -1
  466. package/build/dist/Server/Services/CloudResourceInstanceService.js +66 -0
  467. package/build/dist/Server/Services/CloudResourceInstanceService.js.map +1 -1
  468. package/build/dist/Server/Services/ExceptionAggregationService.js +1 -0
  469. package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
  470. package/build/dist/Server/Services/GoogleSecOpsConnectionRunService.js +9 -0
  471. package/build/dist/Server/Services/GoogleSecOpsConnectionRunService.js.map +1 -0
  472. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +7 -0
  473. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
  474. package/build/dist/Server/Services/Index.js +16 -0
  475. package/build/dist/Server/Services/Index.js.map +1 -1
  476. package/build/dist/Server/Services/LogAggregationService.js +1 -0
  477. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  478. package/build/dist/Server/Services/MetricAggregationService.js +1 -0
  479. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  480. package/build/dist/Server/Services/MobilePasskeyLoginService.js +137 -0
  481. package/build/dist/Server/Services/MobilePasskeyLoginService.js.map +1 -0
  482. package/build/dist/Server/Services/MonitorService.js +34 -1
  483. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  484. package/build/dist/Server/Services/MonitorTemplateService.js +64 -26
  485. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  486. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +124 -1
  487. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  488. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +142 -2
  489. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  490. package/build/dist/Server/Services/OpenTelemetryIngestService.js +28 -13
  491. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  492. package/build/dist/Server/Services/PayAsYouGoBillingService.js +297 -0
  493. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -0
  494. package/build/dist/Server/Services/ProjectService.js +38 -2
  495. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  496. package/build/dist/Server/Services/ServerlessFunctionInstanceService.js +66 -0
  497. package/build/dist/Server/Services/ServerlessFunctionInstanceService.js.map +1 -1
  498. package/build/dist/Server/Services/TeamMemberService.js +85 -28
  499. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  500. package/build/dist/Server/Services/TelemetryIngestionKeyService.js +61 -10
  501. package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
  502. package/build/dist/Server/Services/TelemetryUsageBillingService.js +62 -5
  503. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  504. package/build/dist/Server/Services/TraceAggregationService.js +5 -4
  505. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  506. package/build/dist/Server/Services/UserWebAuthnService.js +345 -113
  507. package/build/dist/Server/Services/UserWebAuthnService.js.map +1 -1
  508. package/build/dist/Server/Services/VMwareResourceService.js +395 -0
  509. package/build/dist/Server/Services/VMwareResourceService.js.map +1 -0
  510. package/build/dist/Server/Services/VMwareVCenterFeedService.js +89 -0
  511. package/build/dist/Server/Services/VMwareVCenterFeedService.js.map +1 -0
  512. package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js +197 -0
  513. package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js.map +1 -0
  514. package/build/dist/Server/Services/VMwareVCenterLabelRuleService.js +13 -0
  515. package/build/dist/Server/Services/VMwareVCenterLabelRuleService.js.map +1 -0
  516. package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js +214 -0
  517. package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js.map +1 -0
  518. package/build/dist/Server/Services/VMwareVCenterOwnerRuleService.js +13 -0
  519. package/build/dist/Server/Services/VMwareVCenterOwnerRuleService.js.map +1 -0
  520. package/build/dist/Server/Services/VMwareVCenterOwnerTeamService.js +129 -0
  521. package/build/dist/Server/Services/VMwareVCenterOwnerTeamService.js.map +1 -0
  522. package/build/dist/Server/Services/VMwareVCenterOwnerUserService.js +122 -0
  523. package/build/dist/Server/Services/VMwareVCenterOwnerUserService.js.map +1 -0
  524. package/build/dist/Server/Services/VMwareVCenterService.js +533 -0
  525. package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -0
  526. package/build/dist/Server/Services/WorkflowVariableService.js +169 -0
  527. package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
  528. package/build/dist/Server/Types/Billing/MeteredPlan/ActiveMonitoringMeteredPlan.js +12 -1
  529. package/build/dist/Server/Types/Billing/MeteredPlan/ActiveMonitoringMeteredPlan.js.map +1 -1
  530. package/build/dist/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.js +25 -2
  531. package/build/dist/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.js.map +1 -1
  532. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  533. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubAgentTaskTrigger.js +275 -0
  534. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubAgentTaskTrigger.js.map +1 -0
  535. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubCommandAuthorizer.js +226 -0
  536. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubCommandAuthorizer.js.map +1 -0
  537. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubCommandParser.js +297 -0
  538. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubCommandParser.js.map +1 -0
  539. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubConversation.js +582 -0
  540. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubConversation.js.map +1 -0
  541. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubReplyComposer.js +275 -0
  542. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubReplyComposer.js.map +1 -0
  543. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubRunReply.js +365 -0
  544. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubRunReply.js.map +1 -0
  545. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubWebhookEvents.js +103 -0
  546. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubWebhookEvents.js.map +1 -0
  547. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubWebhookHandler.js +661 -0
  548. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubWebhookHandler.js.map +1 -0
  549. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +113 -0
  550. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  551. package/build/dist/Server/Utils/DataSource/EgressGuard.js +99 -15
  552. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  553. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +2 -0
  554. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  555. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +18 -0
  556. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
  557. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js +31 -2
  558. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js.map +1 -1
  559. package/build/dist/Server/Utils/LogRedaction.js +2 -1
  560. package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
  561. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +1 -1
  562. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +219 -4
  563. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  564. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +12 -4
  565. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  566. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +5 -3
  567. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
  568. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +22 -9
  569. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
  570. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
  571. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  572. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +21 -0
  573. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  574. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +21 -6
  575. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  576. package/build/dist/Server/Utils/ResourceFeed/ResourceFeedUtil.js +3 -3
  577. package/build/dist/Server/Utils/SecurityEvent/ConnectorErrorMessage.js +9 -9
  578. package/build/dist/Server/Utils/SecurityEvent/ConnectorErrorMessage.js.map +1 -1
  579. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +92 -71
  580. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  581. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +428 -143
  582. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  583. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +376 -0
  584. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -0
  585. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -0
  586. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  587. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
  588. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +136 -1
  589. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  590. package/build/dist/Server/Utils/Telemetry/VMwareSnapshotScan.js +933 -0
  591. package/build/dist/Server/Utils/Telemetry/VMwareSnapshotScan.js.map +1 -0
  592. package/build/dist/Types/AI/CodeFixTaskContext.js +40 -0
  593. package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
  594. package/build/dist/Types/AI/CodeFixTaskType.js +57 -0
  595. package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -1
  596. package/build/dist/Types/Accounts/AccountsLanguage.js +1 -0
  597. package/build/dist/Types/Accounts/AccountsLanguage.js.map +1 -1
  598. package/build/dist/Types/AdminDashboard/AdminDashboardLanguage.js +1 -0
  599. package/build/dist/Types/AdminDashboard/AdminDashboardLanguage.js.map +1 -1
  600. package/build/dist/Types/Cloud/CloudPlatform.js +277 -0
  601. package/build/dist/Types/Cloud/CloudPlatform.js.map +1 -0
  602. package/build/dist/Types/CodeRepository/GitHubCommand.js +52 -0
  603. package/build/dist/Types/CodeRepository/GitHubCommand.js.map +1 -0
  604. package/build/dist/Types/Dashboard/DashboardComponentType.js +2 -0
  605. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  606. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  607. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  608. package/build/dist/Types/Dashboard/DashboardComponents/DashboardVMwareHostListComponent.js +2 -0
  609. package/build/dist/Types/Dashboard/DashboardComponents/DashboardVMwareHostListComponent.js.map +1 -0
  610. package/build/dist/Types/Dashboard/DashboardComponents/DashboardVMwareVirtualMachineListComponent.js +2 -0
  611. package/build/dist/Types/Dashboard/DashboardComponents/DashboardVMwareVirtualMachineListComponent.js.map +1 -0
  612. package/build/dist/Types/Dashboard/DashboardLanguage.js +1 -0
  613. package/build/dist/Types/Dashboard/DashboardLanguage.js.map +1 -1
  614. package/build/dist/Types/Dashboard/DashboardTemplates.js +286 -0
  615. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  616. package/build/dist/Types/Docs/DocsLanguage.js +1 -0
  617. package/build/dist/Types/Docs/DocsLanguage.js.map +1 -1
  618. package/build/dist/Types/Exception/EgressGuardException.js +59 -0
  619. package/build/dist/Types/Exception/EgressGuardException.js.map +1 -0
  620. package/build/dist/Types/Icon/IconProp.js +1 -0
  621. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  622. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +1 -0
  623. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  624. package/build/dist/Types/Monitor/MonitorStep.js +61 -18
  625. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  626. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +1 -0
  627. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -1
  628. package/build/dist/Types/Monitor/MonitorStepVMwareMonitor.js +71 -0
  629. package/build/dist/Types/Monitor/MonitorStepVMwareMonitor.js.map +1 -0
  630. package/build/dist/Types/Monitor/MonitorTemplateSyncField.js +211 -0
  631. package/build/dist/Types/Monitor/MonitorTemplateSyncField.js.map +1 -0
  632. package/build/dist/Types/Monitor/MonitorType.js +23 -0
  633. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  634. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +28 -0
  635. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  636. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +1 -0
  637. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  638. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +2 -0
  639. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  640. package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js +120 -0
  641. package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js.map +1 -1
  642. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +60 -0
  643. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
  644. package/build/dist/Types/Monitor/VMwareAlertTemplates.js +775 -0
  645. package/build/dist/Types/Monitor/VMwareAlertTemplates.js.map +1 -0
  646. package/build/dist/Types/Monitor/VMwareMetricCatalog.js +799 -0
  647. package/build/dist/Types/Monitor/VMwareMetricCatalog.js.map +1 -0
  648. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +25 -0
  649. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  650. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +5 -0
  651. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
  652. package/build/dist/Types/Password.js +63 -5
  653. package/build/dist/Types/Password.js.map +1 -1
  654. package/build/dist/Types/Permission.js +234 -0
  655. package/build/dist/Types/Permission.js.map +1 -1
  656. package/build/dist/Types/Probe/RequestFailedDetails.js +7 -0
  657. package/build/dist/Types/Probe/RequestFailedDetails.js.map +1 -1
  658. package/build/dist/Types/SecurityEvent/GoogleSecOpsDiagnostics.js +2 -0
  659. package/build/dist/Types/SecurityEvent/GoogleSecOpsDiagnostics.js.map +1 -0
  660. package/build/dist/Types/StatusPage/StatusPageLanguage.js +1 -0
  661. package/build/dist/Types/StatusPage/StatusPageLanguage.js.map +1 -1
  662. package/build/dist/Types/Telemetry/EntityType.js +21 -0
  663. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  664. package/build/dist/Types/Telemetry/ServiceType.js +1 -0
  665. package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
  666. package/build/dist/UI/Components/Breadcrumbs/Breadcrumbs.js +2 -2
  667. package/build/dist/UI/Components/Breadcrumbs/Breadcrumbs.js.map +1 -1
  668. package/build/dist/UI/Components/Calendar/Calendar.js +24 -4
  669. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  670. package/build/dist/UI/Components/Forms/BasicForm.js +28 -3
  671. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  672. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +1 -1
  673. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
  674. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -3
  675. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  676. package/build/dist/UI/Components/Forms/ModelForm.js +89 -6
  677. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  678. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +5 -6
  679. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  680. package/build/dist/UI/Components/Icon/Icon.js +14 -0
  681. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  682. package/build/dist/UI/Components/Input/Input.js +37 -8
  683. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  684. package/build/dist/UI/Components/LabelRule/ImportLabelRulesModal.js +228 -0
  685. package/build/dist/UI/Components/LabelRule/ImportLabelRulesModal.js.map +1 -0
  686. package/build/dist/UI/Components/LabelRule/LabelRuleTable.js +102 -0
  687. package/build/dist/UI/Components/LabelRule/LabelRuleTable.js.map +1 -0
  688. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +15 -2
  689. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  690. package/build/dist/UI/Components/ModelTable/ModelTable.js +13 -5
  691. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  692. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +14 -15
  693. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  694. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +2 -0
  695. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  696. package/build/dist/UI/Components/Tooltip/Tooltip.js +33 -4
  697. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  698. package/build/dist/UI/Utils/DashboardStackingLayers.js +44 -0
  699. package/build/dist/UI/Utils/DashboardStackingLayers.js.map +1 -0
  700. package/build/dist/UI/Utils/LabelRuleImportExport.js +195 -0
  701. package/build/dist/UI/Utils/LabelRuleImportExport.js.map +1 -0
  702. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +4 -4
  703. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  704. package/build/dist/UI/Utils/TelemetryService.js +3 -2
  705. package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
  706. package/build/dist/UI/Utils/UseDashboardGridDnd.js +2 -1
  707. package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -1
  708. package/build/dist/UI/Utils/UserEmailRegistrationStatus.js +21 -4
  709. package/build/dist/UI/Utils/UserEmailRegistrationStatus.js.map +1 -1
  710. package/build/dist/UI/Utils/WebAuthn.js +121 -0
  711. package/build/dist/UI/Utils/WebAuthn.js.map +1 -0
  712. package/build/dist/Utils/API.js +29 -0
  713. package/build/dist/Utils/API.js.map +1 -1
  714. package/build/dist/Utils/Dashboard/Components/DashboardVMwareHostListComponent.js +33 -0
  715. package/build/dist/Utils/Dashboard/Components/DashboardVMwareHostListComponent.js.map +1 -0
  716. package/build/dist/Utils/Dashboard/Components/DashboardVMwareResourceListShared.js +54 -0
  717. package/build/dist/Utils/Dashboard/Components/DashboardVMwareResourceListShared.js.map +1 -0
  718. package/build/dist/Utils/Dashboard/Components/DashboardVMwareVirtualMachineListComponent.js +60 -0
  719. package/build/dist/Utils/Dashboard/Components/DashboardVMwareVirtualMachineListComponent.js.map +1 -0
  720. package/build/dist/Utils/Dashboard/Components/Index.js +8 -0
  721. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  722. package/build/dist/Utils/LabelRuleImportExport.js +356 -0
  723. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -0
  724. package/build/dist/Utils/Monitor/MonitorTemplateSyncUtil.js +110 -0
  725. package/build/dist/Utils/Monitor/MonitorTemplateSyncUtil.js.map +1 -0
  726. package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js +6 -0
  727. package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js.map +1 -1
  728. package/build/dist/Utils/Telemetry/CloudInstanceIdentity.js +88 -0
  729. package/build/dist/Utils/Telemetry/CloudInstanceIdentity.js.map +1 -0
  730. package/build/dist/Utils/Telemetry/EntityKey.js +15 -0
  731. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  732. package/build/dist/Utils/Telemetry/EntityRelationship.js +13 -0
  733. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  734. package/package.json +1 -1
@@ -0,0 +1,883 @@
1
+ import CodeRepositoryService from "../../../../Server/Services/CodeRepositoryService";
2
+ import ProjectService from "../../../../Server/Services/ProjectService";
3
+ import GitHubCommandAuthorizer, {
4
+ GitHubAuthorizationOutcome,
5
+ GitHubAuthorizationResult,
6
+ } from "../../../../Server/Utils/CodeRepository/GitHub/GitHubCommandAuthorizer";
7
+ import GitHubConversation, {
8
+ GitHubRepositoryPermission,
9
+ } from "../../../../Server/Utils/CodeRepository/GitHub/GitHubConversation";
10
+ import GitHubInstallationBinding from "../../../../Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding";
11
+ import CodeRepository from "../../../../Models/DatabaseModels/CodeRepository";
12
+ import Project from "../../../../Models/DatabaseModels/Project";
13
+ import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
14
+ import CodeRepositoryType from "../../../../Types/CodeRepository/CodeRepositoryType";
15
+ import ObjectID from "../../../../Types/ObjectID";
16
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
17
+
18
+ /*
19
+ * ---------------------------------------------------------------------------
20
+ * GitHubCommandAuthorizer is the ONLY thing standing between "@oneuptime
21
+ * implement this" typed by a stranger and an agent run that pushes commits,
22
+ * opens pull requests and spends a project's AI budget.
23
+ *
24
+ * Every command arrives from an untrusted party: a connected repository's
25
+ * issue tracker is open to anyone with a GitHub account, and a mention costs
26
+ * nothing to write. So there are four invariants here, and each one is a
27
+ * different incident when it regresses.
28
+ *
29
+ * 1. BOTS ARE NEVER OBEYED, AND NEVER ANSWERED. The app's own comments come
30
+ * back as webhooks. If a bot sender ever reached the resolver, two apps
31
+ * mentioning each other would burn a budget overnight. The guard has to
32
+ * fire before ANY service call — a bot must not even cause a database
33
+ * read.
34
+ *
35
+ * 2. THE ROW'S INSTALLATION ID IS NOT AUTHORITY (GHSA-xx95-gmcf-7q86). A
36
+ * CodeRepository row carries an installation ID, and the single GitHub
37
+ * App JWT this instance holds will mint a write-scoped token for whatever
38
+ * installation it is handed. Whether an installation may act for a
39
+ * project is answered by GitHubInstallationBinding and nowhere else, so
40
+ * an unbound candidate row must be SKIPPED rather than used.
41
+ *
42
+ * 3. ONE COMMENT PRODUCES AT MOST ONE RUN. A repository can be imported by
43
+ * several projects. Acting for all of them turns one mention into several
44
+ * projects' pull requests, so the query is ordered oldest-first and the
45
+ * first bound candidate wins — deterministically, and identically across
46
+ * webhook redeliveries.
47
+ *
48
+ * 4. SILENCE VS. REPLY IS PART OF THE BOUNDARY. A collaborator who hits a
49
+ * switched-off repository gets a reason to show them. Someone WITHOUT
50
+ * write access gets no reason at all, because a guaranteed reply per
51
+ * mention is exactly the amplifier an open issue tracker would hand to
52
+ * anyone with an account. These tests assert the presence and the ABSENCE
53
+ * of `reason` as carefully as they assert the outcome.
54
+ * ---------------------------------------------------------------------------
55
+ */
56
+
57
+ const ORGANIZATION: string = "acme";
58
+ const REPOSITORY: string = "checkout";
59
+ const INSTALLATION_ID: string = "12345678";
60
+
61
+ // The shape of the single argument CodeRepositoryService.findBy is handed.
62
+ interface FindByCallArguments {
63
+ query: {
64
+ organizationName?: string | undefined;
65
+ repositoryName?: string | undefined;
66
+ repositoryHostedAt?: CodeRepositoryType | undefined;
67
+ gitHubAppInstallationId?: string | undefined;
68
+ };
69
+ sort: {
70
+ createdAt?: SortOrder | undefined;
71
+ };
72
+ props: {
73
+ isRoot?: boolean | undefined;
74
+ };
75
+ limit?: number | undefined;
76
+ skip?: number | undefined;
77
+ }
78
+
79
+ interface BindingCallArguments {
80
+ projectId: ObjectID;
81
+ installationId: string;
82
+ }
83
+
84
+ interface PermissionCallArguments {
85
+ installationId: string;
86
+ organizationName: string;
87
+ repositoryName: string;
88
+ username: string;
89
+ }
90
+
91
+ describe("GitHubCommandAuthorizer", () => {
92
+ let projectId: ObjectID;
93
+ let findBySpy: jest.SpyInstance;
94
+ let isBoundSpy: jest.SpyInstance;
95
+ let permissionSpy: jest.SpyInstance;
96
+ let findOneByIdSpy: jest.SpyInstance;
97
+
98
+ beforeEach(() => {
99
+ projectId = ObjectID.generate();
100
+
101
+ // Default world: nothing connected, everything else permissive.
102
+ findBySpy = jest.spyOn(CodeRepositoryService, "findBy");
103
+ findBySpy.mockResolvedValue([]);
104
+
105
+ isBoundSpy = jest.spyOn(
106
+ GitHubInstallationBinding,
107
+ "isInstallationBoundToProject",
108
+ );
109
+ isBoundSpy.mockResolvedValue(true);
110
+
111
+ permissionSpy = jest.spyOn(
112
+ GitHubConversation,
113
+ "getUserRepositoryPermission",
114
+ );
115
+ permissionSpy.mockResolvedValue(GitHubRepositoryPermission.Admin);
116
+
117
+ findOneByIdSpy = jest.spyOn(ProjectService, "findOneById");
118
+ findOneByIdSpy.mockResolvedValue(namedProject("Acme Production"));
119
+ });
120
+
121
+ /*
122
+ * Required, not cosmetic: these spies mutate shared module singletons that
123
+ * outlive the test and would leak into every other test in this worker.
124
+ */
125
+ afterEach(() => {
126
+ jest.restoreAllMocks();
127
+ });
128
+
129
+ function namedProject(name: string): Project {
130
+ const project: Project = new Project();
131
+ project.id = projectId;
132
+ project.name = name;
133
+ return project;
134
+ }
135
+
136
+ // A row with the identity fields set but neither project nor installation.
137
+ function bareRepository(): CodeRepository {
138
+ const repository: CodeRepository = new CodeRepository();
139
+ repository.id = ObjectID.generate();
140
+ repository.organizationName = ORGANIZATION;
141
+ repository.repositoryName = REPOSITORY;
142
+ repository.mainBranchName = "main";
143
+ repository.repositoryHostedAt = CodeRepositoryType.GitHub;
144
+ return repository;
145
+ }
146
+
147
+ function connectedRepository(data?: {
148
+ projectId?: ObjectID | undefined;
149
+ installationId?: string | undefined;
150
+ }): CodeRepository {
151
+ const repository: CodeRepository = bareRepository();
152
+ repository.projectId = data?.projectId || projectId;
153
+ repository.gitHubAppInstallationId =
154
+ data?.installationId || INSTALLATION_ID;
155
+ return repository;
156
+ }
157
+
158
+ /*
159
+ * The commands switch is a nullable boolean column, and "null" is a state
160
+ * TypeScript will not let us assign through the model's own typing. Tests
161
+ * that pin the null-means-enabled semantics need to produce it anyway.
162
+ */
163
+ function setCommandsColumn(
164
+ repository: CodeRepository,
165
+ value: boolean | null | undefined,
166
+ ): void {
167
+ (repository as unknown as Record<string, boolean | null | undefined>)[
168
+ "isGitHubCommandsEnabled"
169
+ ] = value;
170
+ }
171
+
172
+ function mockCandidates(candidates: Array<CodeRepository>): void {
173
+ findBySpy.mockResolvedValue(candidates);
174
+ }
175
+
176
+ function authorize(overrides?: {
177
+ senderLogin?: string | undefined;
178
+ senderType?: string | undefined;
179
+ installationId?: string | undefined;
180
+ }): Promise<GitHubAuthorizationResult> {
181
+ return GitHubCommandAuthorizer.authorize({
182
+ organizationName: ORGANIZATION,
183
+ repositoryName: REPOSITORY,
184
+ installationId: overrides?.installationId ?? INSTALLATION_ID,
185
+ senderLogin: overrides?.senderLogin ?? "octo-dev",
186
+ senderType: overrides?.senderType ?? "User",
187
+ });
188
+ }
189
+
190
+ function resolve(): Promise<CodeRepository | null> {
191
+ return GitHubCommandAuthorizer.resolveConnectedRepository({
192
+ organizationName: ORGANIZATION,
193
+ repositoryName: REPOSITORY,
194
+ installationId: INSTALLATION_ID,
195
+ });
196
+ }
197
+
198
+ function findByCall(): FindByCallArguments {
199
+ return findBySpy.mock.calls[0]![0] as FindByCallArguments;
200
+ }
201
+
202
+ function bindingCall(callNumber: number): BindingCallArguments {
203
+ return isBoundSpy.mock.calls[callNumber - 1]![0] as BindingCallArguments;
204
+ }
205
+
206
+ function permissionCall(): PermissionCallArguments {
207
+ return permissionSpy.mock.calls[0]![0] as PermissionCallArguments;
208
+ }
209
+
210
+ function expectNoServiceCallsAtAll(): void {
211
+ expect(findBySpy).not.toHaveBeenCalled();
212
+ expect(isBoundSpy).not.toHaveBeenCalled();
213
+ expect(permissionSpy).not.toHaveBeenCalled();
214
+ expect(findOneByIdSpy).not.toHaveBeenCalled();
215
+ }
216
+
217
+ /*
218
+ * Invariant 1. The app's own comment arrives here as a webhook, as does
219
+ * every other app's. A single reply to a bot is the first half of a loop
220
+ * that has no second half.
221
+ */
222
+ describe("the bot loop guard", () => {
223
+ test("ignores a sender GitHub itself labels as a Bot", async () => {
224
+ const result: GitHubAuthorizationResult = await authorize({
225
+ senderLogin: "dependabot",
226
+ senderType: "Bot",
227
+ });
228
+
229
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Ignore);
230
+ });
231
+
232
+ test("ignores a login ending in [bot] even when the sender type says User", async () => {
233
+ const result: GitHubAuthorizationResult = await authorize({
234
+ senderLogin: "oneuptime[bot]",
235
+ senderType: "User",
236
+ });
237
+
238
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Ignore);
239
+ });
240
+
241
+ test("ignores its own bot login whatever case GitHub sends it in", async () => {
242
+ const result: GitHubAuthorizationResult = await authorize({
243
+ senderLogin: "OneUptime[BOT]",
244
+ senderType: "User",
245
+ });
246
+
247
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Ignore);
248
+ });
249
+
250
+ test("ignores an empty sender login", async () => {
251
+ const result: GitHubAuthorizationResult = await authorize({
252
+ senderLogin: "",
253
+ senderType: "User",
254
+ });
255
+
256
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Ignore);
257
+ });
258
+
259
+ test("ignores an empty login even when the webhook omits the sender type", async () => {
260
+ const result: GitHubAuthorizationResult =
261
+ await GitHubCommandAuthorizer.authorize({
262
+ organizationName: ORGANIZATION,
263
+ repositoryName: REPOSITORY,
264
+ installationId: INSTALLATION_ID,
265
+ senderLogin: "",
266
+ senderType: undefined,
267
+ });
268
+
269
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Ignore);
270
+ });
271
+
272
+ /*
273
+ * The mirror image, and the more dangerous direction. A webhook that
274
+ * carries no sender.type is not evidence of a bot, and treating it as one
275
+ * would silently drop a collaborator's command.
276
+ */
277
+ test("still serves a human whose webhook carries no sender type", async () => {
278
+ mockCandidates([connectedRepository()]);
279
+
280
+ const result: GitHubAuthorizationResult =
281
+ await GitHubCommandAuthorizer.authorize({
282
+ organizationName: ORGANIZATION,
283
+ repositoryName: REPOSITORY,
284
+ installationId: INSTALLATION_ID,
285
+ senderLogin: "octo-dev",
286
+ senderType: undefined,
287
+ });
288
+
289
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Allowed);
290
+ });
291
+
292
+ /*
293
+ * The guard is worth nothing if it fires AFTER the work. A bot comment
294
+ * must not cost a database read, a binding check or a GitHub API call —
295
+ * otherwise two chatty apps still generate load in a tight loop even
296
+ * though neither is obeyed.
297
+ */
298
+ test("makes no service call at all for a bot, even on a connected repository", async () => {
299
+ mockCandidates([connectedRepository()]);
300
+
301
+ await authorize({ senderLogin: "renovate[bot]", senderType: "Bot" });
302
+
303
+ expectNoServiceCallsAtAll();
304
+ });
305
+
306
+ test("makes no service call at all for an empty sender login", async () => {
307
+ mockCandidates([connectedRepository()]);
308
+
309
+ await authorize({ senderLogin: "", senderType: "User" });
310
+
311
+ expectNoServiceCallsAtAll();
312
+ });
313
+
314
+ test("returns no repository to act on when it ignores a bot", async () => {
315
+ mockCandidates([connectedRepository()]);
316
+
317
+ const result: GitHubAuthorizationResult = await authorize({
318
+ senderLogin: "oneuptime[bot]",
319
+ senderType: "Bot",
320
+ });
321
+
322
+ expect(result.codeRepository).toBeUndefined();
323
+ expect(result.projectName).toBeUndefined();
324
+ expect(result.reason).toBeUndefined();
325
+ });
326
+
327
+ /*
328
+ * The other half of the guard: it must not be so broad that it silently
329
+ * swallows real people. A dropped command from a human reads to them as
330
+ * "the integration is broken", and there is no log line in their thread.
331
+ */
332
+ test("does not mistake a human whose login merely contains 'bot' for a bot", async () => {
333
+ mockCandidates([connectedRepository()]);
334
+
335
+ const result: GitHubAuthorizationResult = await authorize({
336
+ senderLogin: "robotnik",
337
+ senderType: "User",
338
+ });
339
+
340
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Allowed);
341
+ expect(findBySpy).toHaveBeenCalled();
342
+ });
343
+
344
+ test("does not ignore a login with [bot] in the middle rather than at the end", async () => {
345
+ mockCandidates([connectedRepository()]);
346
+
347
+ const result: GitHubAuthorizationResult = await authorize({
348
+ senderLogin: "not-a[bot]-account",
349
+ senderType: "User",
350
+ });
351
+
352
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Allowed);
353
+ });
354
+ });
355
+
356
+ describe("resolveConnectedRepository", () => {
357
+ /*
358
+ * All four filters matter. Dropping the installation would let any
359
+ * installation act on a repository name it does not own; dropping
360
+ * repositoryHostedAt would match a GitLab row of the same name.
361
+ */
362
+ test("filters on organization, repository, GitHub and installation together", async () => {
363
+ await resolve();
364
+
365
+ expect(findBySpy).toHaveBeenCalledTimes(1);
366
+ expect(findByCall().query.organizationName).toBe(ORGANIZATION);
367
+ expect(findByCall().query.repositoryName).toBe(REPOSITORY);
368
+ expect(findByCall().query.repositoryHostedAt).toBe(
369
+ CodeRepositoryType.GitHub,
370
+ );
371
+ expect(findByCall().query.gitHubAppInstallationId).toBe(INSTALLATION_ID);
372
+ });
373
+
374
+ /*
375
+ * Invariant 3. Ascending createdAt is what makes "the oldest connection
376
+ * wins" a fact rather than a coin flip. Flip this to Descending and a
377
+ * webhook redelivery can land in a different project than the first
378
+ * delivery did.
379
+ */
380
+ test("asks for the candidates oldest first, so the winner is deterministic", async () => {
381
+ await resolve();
382
+
383
+ expect(findByCall().sort.createdAt).toBe(SortOrder.Ascending);
384
+ });
385
+
386
+ test("reads as root, so nobody's permissions can hide a connection", async () => {
387
+ await resolve();
388
+
389
+ expect(findByCall().props.isRoot).toBe(true);
390
+ });
391
+
392
+ test("returns null when the repository is connected to no project", async () => {
393
+ mockCandidates([]);
394
+
395
+ await expect(resolve()).resolves.toBeNull();
396
+ });
397
+
398
+ test("returns the candidate when its installation is bound to its project", async () => {
399
+ const repository: CodeRepository = connectedRepository();
400
+ mockCandidates([repository]);
401
+
402
+ await expect(resolve()).resolves.toBe(repository);
403
+ });
404
+
405
+ /*
406
+ * Invariant 2, the core of it. The row says which installation it belongs
407
+ * to; the row is not allowed to be believed. A row whose installation ID
408
+ * was planted must be stepped over, and a legitimate row behind it must
409
+ * still be found.
410
+ */
411
+ test("skips an unbound candidate and uses the bound one behind it", async () => {
412
+ const plantedProjectId: ObjectID = ObjectID.generate();
413
+ const planted: CodeRepository = connectedRepository({
414
+ projectId: plantedProjectId,
415
+ });
416
+ const legitimate: CodeRepository = connectedRepository();
417
+
418
+ mockCandidates([planted, legitimate]);
419
+ isBoundSpy.mockResolvedValueOnce(false).mockResolvedValueOnce(true);
420
+
421
+ await expect(resolve()).resolves.toBe(legitimate);
422
+ });
423
+
424
+ test("returns null when no candidate's installation is bound to its project", async () => {
425
+ mockCandidates([
426
+ connectedRepository({ projectId: ObjectID.generate() }),
427
+ connectedRepository({ projectId: ObjectID.generate() }),
428
+ ]);
429
+ isBoundSpy.mockResolvedValue(false);
430
+
431
+ await expect(resolve()).resolves.toBeNull();
432
+ });
433
+
434
+ test("asks the binding about the candidate's OWN project and installation", async () => {
435
+ mockCandidates([
436
+ connectedRepository({
437
+ projectId: projectId,
438
+ installationId: INSTALLATION_ID,
439
+ }),
440
+ ]);
441
+
442
+ await resolve();
443
+
444
+ expect(isBoundSpy).toHaveBeenCalledTimes(1);
445
+ expect(bindingCall(1).projectId.toString()).toBe(projectId.toString());
446
+ expect(bindingCall(1).installationId).toBe(INSTALLATION_ID);
447
+ });
448
+
449
+ /*
450
+ * Invariant 3 again, at the point it actually bites. Three projects have
451
+ * imported this repository and all three bindings are genuine. Only the
452
+ * oldest may act, and the loop must stop the moment it finds it — one
453
+ * mention, one run, one pull request.
454
+ */
455
+ test("stops at the FIRST bound candidate when several are bound", async () => {
456
+ const oldest: CodeRepository = connectedRepository({
457
+ projectId: projectId,
458
+ });
459
+ const middle: CodeRepository = connectedRepository({
460
+ projectId: ObjectID.generate(),
461
+ });
462
+ const newest: CodeRepository = connectedRepository({
463
+ projectId: ObjectID.generate(),
464
+ });
465
+
466
+ mockCandidates([oldest, middle, newest]);
467
+
468
+ const resolved: CodeRepository | null = await resolve();
469
+
470
+ expect(resolved).toBe(oldest);
471
+ expect(resolved).not.toBe(middle);
472
+ expect(resolved).not.toBe(newest);
473
+ expect(isBoundSpy).toHaveBeenCalledTimes(1);
474
+ });
475
+
476
+ // A half-populated row cannot be permitted by accident.
477
+ test("skips a row carrying no project id, without asking the binding about it", async () => {
478
+ const orphan: CodeRepository = bareRepository();
479
+ orphan.gitHubAppInstallationId = INSTALLATION_ID;
480
+ const legitimate: CodeRepository = connectedRepository();
481
+
482
+ mockCandidates([orphan, legitimate]);
483
+
484
+ await expect(resolve()).resolves.toBe(legitimate);
485
+ expect(isBoundSpy).toHaveBeenCalledTimes(1);
486
+ expect(bindingCall(1).projectId.toString()).toBe(projectId.toString());
487
+ });
488
+
489
+ test("skips a row carrying no installation id, without asking the binding about it", async () => {
490
+ const halfPopulated: CodeRepository = bareRepository();
491
+ halfPopulated.projectId = ObjectID.generate();
492
+ const legitimate: CodeRepository = connectedRepository();
493
+
494
+ mockCandidates([halfPopulated, legitimate]);
495
+
496
+ await expect(resolve()).resolves.toBe(legitimate);
497
+ expect(isBoundSpy).toHaveBeenCalledTimes(1);
498
+ });
499
+
500
+ test("returns null when the only candidate is half-populated", async () => {
501
+ mockCandidates([bareRepository()]);
502
+
503
+ await expect(resolve()).resolves.toBeNull();
504
+ expect(isBoundSpy).not.toHaveBeenCalled();
505
+ });
506
+ });
507
+
508
+ /*
509
+ * The switch was added after the repositories were. Defaulting existing
510
+ * rows to "off" would mean the feature does nothing anywhere until every
511
+ * repository is visited by hand, so a null/absent column reads as enabled.
512
+ * Only an explicit `false` turns it off.
513
+ */
514
+ describe("areCommandsEnabled", () => {
515
+ test("treats an untouched column as ENABLED", () => {
516
+ const repository: CodeRepository = connectedRepository();
517
+
518
+ expect(repository.isGitHubCommandsEnabled).toBeUndefined();
519
+ expect(GitHubCommandAuthorizer.areCommandsEnabled(repository)).toBe(true);
520
+ });
521
+
522
+ test("treats a NULL column as enabled, not as disabled", () => {
523
+ const repository: CodeRepository = connectedRepository();
524
+ setCommandsColumn(repository, null);
525
+
526
+ expect(GitHubCommandAuthorizer.areCommandsEnabled(repository)).toBe(true);
527
+ });
528
+
529
+ test("treats an explicitly undefined column as enabled", () => {
530
+ const repository: CodeRepository = connectedRepository();
531
+ setCommandsColumn(repository, undefined);
532
+
533
+ expect(GitHubCommandAuthorizer.areCommandsEnabled(repository)).toBe(true);
534
+ });
535
+
536
+ test("returns true when the column is explicitly true", () => {
537
+ const repository: CodeRepository = connectedRepository();
538
+ setCommandsColumn(repository, true);
539
+
540
+ expect(GitHubCommandAuthorizer.areCommandsEnabled(repository)).toBe(true);
541
+ });
542
+
543
+ // The one and only value that switches the feature off.
544
+ test("returns false only for an explicit false", () => {
545
+ const repository: CodeRepository = connectedRepository();
546
+ setCommandsColumn(repository, false);
547
+
548
+ expect(GitHubCommandAuthorizer.areCommandsEnabled(repository)).toBe(
549
+ false,
550
+ );
551
+ });
552
+ });
553
+
554
+ describe("authorize", () => {
555
+ describe("a repository this instance has no standing in", () => {
556
+ test("ignores a command on a repository connected to no project", async () => {
557
+ mockCandidates([]);
558
+
559
+ const result: GitHubAuthorizationResult = await authorize();
560
+
561
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Ignore);
562
+ expect(result.codeRepository).toBeUndefined();
563
+ });
564
+
565
+ /*
566
+ * Silence, not a reply. The app is installed somewhere it was never
567
+ * connected — commenting there would be an uninvited bot posting in
568
+ * someone else's repository.
569
+ */
570
+ test("says nothing back when the repository is not connected", async () => {
571
+ mockCandidates([]);
572
+
573
+ const result: GitHubAuthorizationResult = await authorize();
574
+
575
+ expect(result.reason).toBeUndefined();
576
+ });
577
+
578
+ test("never asks GitHub about the sender when nothing is connected", async () => {
579
+ mockCandidates([]);
580
+
581
+ await authorize();
582
+
583
+ expect(permissionSpy).not.toHaveBeenCalled();
584
+ });
585
+
586
+ /*
587
+ * Invariant 2 end to end: an unbound row must produce exactly the same
588
+ * silence as no row at all, and must never reach the permission check
589
+ * with somebody else's installation.
590
+ */
591
+ test("ignores a command whose only candidate row is unbound", async () => {
592
+ mockCandidates([connectedRepository()]);
593
+ isBoundSpy.mockResolvedValue(false);
594
+
595
+ const result: GitHubAuthorizationResult = await authorize();
596
+
597
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Ignore);
598
+ expect(result.codeRepository).toBeUndefined();
599
+ expect(permissionSpy).not.toHaveBeenCalled();
600
+ });
601
+ });
602
+
603
+ describe("a repository with commands switched off", () => {
604
+ function disabledRepository(): CodeRepository {
605
+ const repository: CodeRepository = connectedRepository();
606
+ setCommandsColumn(repository, false);
607
+ return repository;
608
+ }
609
+
610
+ test("returns CommandsDisabled", async () => {
611
+ mockCandidates([disabledRepository()]);
612
+
613
+ const result: GitHubAuthorizationResult = await authorize();
614
+
615
+ expect(result.outcome).toBe(
616
+ GitHubAuthorizationOutcome.CommandsDisabled,
617
+ );
618
+ });
619
+
620
+ /*
621
+ * This outcome IS answered in the thread — a collaborator who gets
622
+ * silence assumes the integration is broken and files a bug — so the
623
+ * reason has to exist and has to say where the switch lives.
624
+ */
625
+ test("carries a reason that points the collaborator at the switch", async () => {
626
+ mockCandidates([disabledRepository()]);
627
+
628
+ const result: GitHubAuthorizationResult = await authorize();
629
+
630
+ expect(result.reason).toBeDefined();
631
+ expect(result.reason!.length).toBeGreaterThan(0);
632
+ expect(result.reason).toContain("OneUptime");
633
+ });
634
+
635
+ test("carries the repository, so the reply can name what it refused", async () => {
636
+ const repository: CodeRepository = disabledRepository();
637
+ mockCandidates([repository]);
638
+
639
+ const result: GitHubAuthorizationResult = await authorize();
640
+
641
+ expect(result.codeRepository).toBe(repository);
642
+ });
643
+
644
+ /*
645
+ * The switch is checked BEFORE GitHub is asked anything. A disabled
646
+ * repository must not turn every drive-by mention into an API call
647
+ * against someone's rate limit.
648
+ */
649
+ test("never asks GitHub for the sender's permission", async () => {
650
+ mockCandidates([disabledRepository()]);
651
+
652
+ await authorize();
653
+
654
+ expect(permissionSpy).not.toHaveBeenCalled();
655
+ });
656
+
657
+ test("still runs when the column is null, because null means enabled", async () => {
658
+ const repository: CodeRepository = connectedRepository();
659
+ setCommandsColumn(repository, null);
660
+ mockCandidates([repository]);
661
+
662
+ const result: GitHubAuthorizationResult = await authorize();
663
+
664
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Allowed);
665
+ });
666
+ });
667
+
668
+ describe("the repository permission matrix", () => {
669
+ /*
670
+ * Write is the floor: it is the permission that lets a person push to
671
+ * the branch the agent would push to, so anyone who could do the work by
672
+ * hand may ask the app to do it.
673
+ */
674
+ test.each([
675
+ GitHubRepositoryPermission.Admin,
676
+ GitHubRepositoryPermission.Maintain,
677
+ GitHubRepositoryPermission.Write,
678
+ ])(
679
+ "allows a command from a collaborator with %s access",
680
+ async (permission: GitHubRepositoryPermission) => {
681
+ mockCandidates([connectedRepository()]);
682
+ permissionSpy.mockResolvedValue(permission);
683
+
684
+ const result: GitHubAuthorizationResult = await authorize();
685
+
686
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Allowed);
687
+ },
688
+ );
689
+
690
+ /*
691
+ * Read and triage can both comment on the thread, which is exactly why
692
+ * neither may command the app: commenting is not a permission to push.
693
+ */
694
+ test.each([
695
+ GitHubRepositoryPermission.Read,
696
+ GitHubRepositoryPermission.Triage,
697
+ GitHubRepositoryPermission.None,
698
+ ])(
699
+ "refuses a command from someone with only %s access",
700
+ async (permission: GitHubRepositoryPermission) => {
701
+ mockCandidates([connectedRepository()]);
702
+ permissionSpy.mockResolvedValue(permission);
703
+
704
+ const result: GitHubAuthorizationResult = await authorize();
705
+
706
+ expect(result.outcome).toBe(
707
+ GitHubAuthorizationOutcome.InsufficientPermission,
708
+ );
709
+ },
710
+ );
711
+
712
+ /*
713
+ * Invariant 4. No reason means the caller has nothing to post, which is
714
+ * the whole point: a guaranteed reply per mention would make the app a
715
+ * comment-poster any GitHub account could drive.
716
+ */
717
+ test.each([
718
+ GitHubRepositoryPermission.Read,
719
+ GitHubRepositoryPermission.Triage,
720
+ GitHubRepositoryPermission.None,
721
+ ])(
722
+ "gives the caller nothing to say back to someone with %s access",
723
+ async (permission: GitHubRepositoryPermission) => {
724
+ mockCandidates([connectedRepository()]);
725
+ permissionSpy.mockResolvedValue(permission);
726
+
727
+ const result: GitHubAuthorizationResult = await authorize();
728
+
729
+ expect(result.reason).toBeUndefined();
730
+ },
731
+ );
732
+
733
+ // The caller still needs the row — to react on the comment, not to reply.
734
+ test("still returns the repository when the permission is insufficient", async () => {
735
+ const repository: CodeRepository = connectedRepository();
736
+ mockCandidates([repository]);
737
+ permissionSpy.mockResolvedValue(GitHubRepositoryPermission.Read);
738
+
739
+ const result: GitHubAuthorizationResult = await authorize();
740
+
741
+ expect(result.codeRepository).toBe(repository);
742
+ });
743
+
744
+ /*
745
+ * A refused sender must not learn which OneUptime project the
746
+ * repository belongs to — that is internal naming, and the refusal is
747
+ * not answered in the thread anyway.
748
+ */
749
+ test("does not attribute a project to a refused sender", async () => {
750
+ mockCandidates([connectedRepository()]);
751
+ permissionSpy.mockResolvedValue(GitHubRepositoryPermission.Triage);
752
+
753
+ const result: GitHubAuthorizationResult = await authorize();
754
+
755
+ expect(result.projectName).toBeUndefined();
756
+ });
757
+
758
+ test("asks GitHub about the exact sender, repository and installation", async () => {
759
+ mockCandidates([connectedRepository()]);
760
+
761
+ await authorize({ senderLogin: "octo-dev" });
762
+
763
+ expect(permissionSpy).toHaveBeenCalledTimes(1);
764
+ expect(permissionCall().username).toBe("octo-dev");
765
+ expect(permissionCall().organizationName).toBe(ORGANIZATION);
766
+ expect(permissionCall().repositoryName).toBe(REPOSITORY);
767
+ expect(permissionCall().installationId).toBe(INSTALLATION_ID);
768
+ });
769
+
770
+ /*
771
+ * GitHub logins are matched exactly by the collaborator API. Any
772
+ * normalisation applied here — casing, trimming, stripping non-ASCII —
773
+ * would ask about a DIFFERENT account than the one that commented, and
774
+ * that account may have write access when the commenter does not.
775
+ */
776
+ test("passes an unusual sender login through to GitHub untouched", async () => {
777
+ mockCandidates([connectedRepository()]);
778
+
779
+ await authorize({ senderLogin: "Ünïcodé-Dev" });
780
+
781
+ expect(permissionCall().username).toBe("Ünïcodé-Dev");
782
+ });
783
+
784
+ test("does not lowercase a mixed-case sender login before checking it", async () => {
785
+ mockCandidates([connectedRepository()]);
786
+
787
+ await authorize({ senderLogin: "OctoDev" });
788
+
789
+ expect(permissionCall().username).toBe("OctoDev");
790
+ });
791
+ });
792
+
793
+ describe("project attribution on an allowed command", () => {
794
+ test("names the project the app is about to act for", async () => {
795
+ mockCandidates([connectedRepository()]);
796
+
797
+ const result: GitHubAuthorizationResult = await authorize();
798
+
799
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Allowed);
800
+ expect(result.projectName).toBe("Acme Production");
801
+ });
802
+
803
+ /*
804
+ * Attribution follows the WINNING candidate. Naming the first row's
805
+ * project while acting for the second one's would tell a team the wrong
806
+ * budget was about to be spent.
807
+ */
808
+ test("names the project of the bound candidate, not of a skipped one", async () => {
809
+ const skippedProjectId: ObjectID = ObjectID.generate();
810
+ mockCandidates([
811
+ connectedRepository({ projectId: skippedProjectId }),
812
+ connectedRepository({ projectId: projectId }),
813
+ ]);
814
+ isBoundSpy.mockResolvedValueOnce(false).mockResolvedValueOnce(true);
815
+
816
+ await authorize();
817
+
818
+ const lookup: { id: ObjectID } = findOneByIdSpy.mock.calls[0]![0] as {
819
+ id: ObjectID;
820
+ };
821
+
822
+ expect(lookup.id.toString()).toBe(projectId.toString());
823
+ expect(lookup.id.toString()).not.toBe(skippedProjectId.toString());
824
+ });
825
+
826
+ /*
827
+ * A name is a nicety. Refusing to run a command a collaborator is
828
+ * entitled to, because a display-name lookup failed, would be a far
829
+ * worse bug than an unattributed acknowledgement.
830
+ */
831
+ test("still allows the command when the project lookup throws", async () => {
832
+ mockCandidates([connectedRepository()]);
833
+ findOneByIdSpy.mockRejectedValue(new Error("database unreachable"));
834
+
835
+ const result: GitHubAuthorizationResult = await authorize();
836
+
837
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Allowed);
838
+ expect(result.projectName).toBeUndefined();
839
+ });
840
+
841
+ test("still allows the command when the project row is missing", async () => {
842
+ mockCandidates([connectedRepository()]);
843
+ findOneByIdSpy.mockResolvedValue(null);
844
+
845
+ const result: GitHubAuthorizationResult = await authorize();
846
+
847
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Allowed);
848
+ expect(result.projectName).toBeUndefined();
849
+ });
850
+
851
+ // An empty name is not a name — it would render as an empty attribution.
852
+ test("reports no project name rather than an empty one", async () => {
853
+ mockCandidates([connectedRepository()]);
854
+ findOneByIdSpy.mockResolvedValue(namedProject(""));
855
+
856
+ const result: GitHubAuthorizationResult = await authorize();
857
+
858
+ expect(result.projectName).toBeUndefined();
859
+ });
860
+
861
+ // A row with no project has no budget to bill and no project to name.
862
+ test("skips the lookup entirely for a row with no project id", async () => {
863
+ const orphan: CodeRepository = bareRepository();
864
+ orphan.gitHubAppInstallationId = INSTALLATION_ID;
865
+ mockCandidates([orphan]);
866
+
867
+ const result: GitHubAuthorizationResult = await authorize();
868
+
869
+ expect(result.outcome).toBe(GitHubAuthorizationOutcome.Ignore);
870
+ expect(findOneByIdSpy).not.toHaveBeenCalled();
871
+ });
872
+
873
+ test("returns the repository the command should run against", async () => {
874
+ const repository: CodeRepository = connectedRepository();
875
+ mockCandidates([repository]);
876
+
877
+ const result: GitHubAuthorizationResult = await authorize();
878
+
879
+ expect(result.codeRepository).toBe(repository);
880
+ });
881
+ });
882
+ });
883
+ });