@oneuptime/common 12.0.22 → 12.0.24

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 (345) hide show
  1. package/Models/DatabaseModels/DetectionRule.ts +55 -0
  2. package/Models/DatabaseModels/Index.ts +2 -0
  3. package/Models/DatabaseModels/NetworkDevice.ts +44 -0
  4. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +584 -0
  5. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +105 -0
  6. package/Models/DatabaseModels/Project.ts +68 -0
  7. package/Models/DatabaseModels/User.ts +68 -0
  8. package/Server/API/TelemetryAPI.ts +29 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.ts +60 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.ts +26 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.ts +26 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  16. package/Server/Middleware/GzipRequestBody.ts +311 -0
  17. package/Server/Middleware/MultipartFormData.ts +87 -3
  18. package/Server/Services/DetectionRuleService.ts +73 -6
  19. package/Server/Services/Index.ts +207 -0
  20. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +917 -0
  21. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +181 -0
  22. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
  23. package/Server/Services/NetworkDeviceService.ts +148 -0
  24. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  25. package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
  26. package/Server/Services/ProjectService.ts +29 -19
  27. package/Server/Services/SecurityEventService.ts +90 -13
  28. package/Server/Services/StatusPageResourceService.ts +223 -0
  29. package/Server/Services/TelemetryAttributeService.ts +17 -0
  30. package/Server/Services/UserService.ts +4 -6
  31. package/Server/Types/Markdown.ts +161 -67
  32. package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
  33. package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
  34. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
  35. package/Server/Utils/Attribution.ts +4 -0
  36. package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
  37. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +39 -1
  38. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
  39. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
  40. package/Server/Utils/StartServer.ts +12 -32
  41. package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
  42. package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
  43. package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
  44. package/Server/Views/Partials/Head.ejs +2 -1
  45. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
  46. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
  47. package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
  48. package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
  49. package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
  50. package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
  51. package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
  52. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
  53. package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
  54. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
  55. package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
  56. package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
  57. package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
  58. package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
  59. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
  60. package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
  61. package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
  62. package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
  63. package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
  64. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
  65. package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
  66. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
  67. package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
  68. package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
  69. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
  70. package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
  71. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
  72. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
  73. package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
  74. package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
  75. package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
  76. package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +205 -0
  77. package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
  78. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
  79. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
  80. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +933 -0
  81. package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
  82. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
  83. package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
  84. package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
  85. package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
  86. package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
  87. package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
  88. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
  89. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
  91. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +97 -1
  92. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
  93. package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
  94. package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
  95. package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
  96. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
  97. package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
  98. package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
  99. package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
  100. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
  101. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
  102. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
  103. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
  104. package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
  105. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
  106. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
  107. package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
  108. package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
  109. package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
  110. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
  111. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
  112. package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
  113. package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
  114. package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
  115. package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
  116. package/Tests/UI/Components/SideOver.test.tsx +1 -1
  117. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
  118. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
  119. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
  120. package/Tests/UI/Components/Toggle.test.tsx +25 -0
  121. package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
  122. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
  123. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +516 -0
  124. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +272 -0
  125. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
  126. package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
  127. package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +164 -0
  128. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
  129. package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
  130. package/Types/Code/YamlSyntax.ts +396 -0
  131. package/Types/Exception/ExceptionCode.ts +1 -0
  132. package/Types/Exception/PayloadTooLargeException.ts +8 -0
  133. package/Types/Marketing/Attribution.ts +10 -0
  134. package/Types/Marketing/MarketingEvent.ts +4 -0
  135. package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
  136. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
  137. package/Types/NetworkAutomation/RuleRunResult.ts +84 -0
  138. package/Types/Permission.ts +47 -0
  139. package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
  140. package/Types/Telemetry/LlmMetricConventions.ts +124 -0
  141. package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
  142. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
  143. package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
  144. package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
  145. package/UI/Components/Detail/Detail.tsx +10 -0
  146. package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
  147. package/UI/Components/Forms/Fields/FormField.tsx +31 -0
  148. package/UI/Components/Forms/Types/Field.ts +8 -0
  149. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
  150. package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
  151. package/UI/Components/Forms/Validation.ts +46 -0
  152. package/UI/Components/Input/Input.tsx +3 -0
  153. package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
  154. package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
  155. package/UI/Components/ModelTable/Column.ts +8 -0
  156. package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
  157. package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
  158. package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
  159. package/UI/Components/SideOver/SideOver.tsx +5 -5
  160. package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
  161. package/UI/Components/StatusPage/ResourceList.tsx +197 -38
  162. package/UI/Components/Toggle/Toggle.tsx +9 -1
  163. package/UI/Components/Types/FieldType.ts +1 -0
  164. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
  165. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +257 -0
  166. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +183 -0
  167. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +95 -0
  168. package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +66 -0
  169. package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
  170. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +82 -0
  171. package/Utils/StatusPage/ResourceExplorer.ts +258 -0
  172. package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
  173. package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
  174. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  175. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  176. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  177. package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
  178. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  179. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +609 -0
  180. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -0
  181. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +97 -0
  182. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/Project.js +76 -0
  184. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/User.js +76 -0
  186. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  187. package/build/dist/Server/API/TelemetryAPI.js +17 -0
  188. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js +35 -0
  190. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js.map +1 -0
  191. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js +19 -0
  192. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js.map +1 -0
  193. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js +19 -0
  194. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js.map +1 -0
  195. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
  196. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
  197. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
  198. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
  199. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
  200. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
  201. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  202. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  203. package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
  204. package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
  205. package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
  206. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  207. package/build/dist/Server/Services/DetectionRuleService.js +49 -4
  208. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  209. package/build/dist/Server/Services/Index.js +206 -0
  210. package/build/dist/Server/Services/Index.js.map +1 -1
  211. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +711 -0
  212. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -0
  213. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +155 -0
  214. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -0
  215. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
  216. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  217. package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
  218. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  219. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
  220. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  221. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
  222. package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
  223. package/build/dist/Server/Services/ProjectService.js +25 -45
  224. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  225. package/build/dist/Server/Services/SecurityEventService.js +57 -5
  226. package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
  227. package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
  228. package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
  229. package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
  230. package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
  231. package/build/dist/Server/Services/UserService.js +2 -12
  232. package/build/dist/Server/Services/UserService.js.map +1 -1
  233. package/build/dist/Server/Types/Markdown.js +147 -61
  234. package/build/dist/Server/Types/Markdown.js.map +1 -1
  235. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
  236. package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
  237. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
  238. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  239. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
  240. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  241. package/build/dist/Server/Utils/Attribution.js.map +1 -1
  242. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
  243. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +26 -0
  245. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  246. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
  247. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  248. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
  249. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  250. package/build/dist/Server/Utils/StartServer.js +12 -21
  251. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  252. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
  253. package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
  254. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
  255. package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
  256. package/build/dist/Types/Code/YamlSyntax.js +212 -0
  257. package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
  258. package/build/dist/Types/Exception/ExceptionCode.js +1 -0
  259. package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
  260. package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
  261. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
  262. package/build/dist/Types/Marketing/Attribution.js +10 -0
  263. package/build/dist/Types/Marketing/Attribution.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
  265. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  266. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
  267. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
  268. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +24 -0
  269. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
  270. package/build/dist/Types/Permission.js +41 -0
  271. package/build/dist/Types/Permission.js.map +1 -1
  272. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
  273. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  274. package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
  275. package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
  276. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
  277. package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
  278. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
  279. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  280. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
  281. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  282. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
  283. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
  284. package/build/dist/UI/Components/Detail/Detail.js +9 -0
  285. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  286. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
  287. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  288. package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
  289. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  290. package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
  291. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
  292. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  293. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
  294. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
  295. package/build/dist/UI/Components/Forms/Validation.js +27 -0
  296. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  297. package/build/dist/UI/Components/Input/Input.js +1 -1
  298. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  299. package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
  300. package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
  301. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
  302. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  303. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
  304. package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
  305. package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
  306. package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
  307. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
  308. package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
  309. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
  310. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  311. package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
  312. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  313. package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
  314. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  315. package/build/dist/UI/Components/Types/FieldType.js +1 -0
  316. package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
  317. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
  318. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  319. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +149 -0
  320. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -0
  321. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +121 -0
  322. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -0
  323. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +75 -0
  324. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -0
  325. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +59 -0
  326. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -0
  327. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
  328. package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
  329. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +60 -0
  330. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  331. package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
  332. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
  333. package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
  334. package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
  335. package/jest.config.json +2 -0
  336. package/package.json +1 -1
  337. package/tsconfig.json +2 -1
  338. package/Tests/UI/Components/Toast.test.tsx +0 -197
  339. package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
  340. package/UI/Components/Toast/Toast.tsx +0 -189
  341. package/UI/Components/Toast/ToastInit.tsx +0 -110
  342. package/build/dist/UI/Components/Toast/Toast.js +0 -123
  343. package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
  344. package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
  345. package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
@@ -0,0 +1,311 @@
1
+ import ServerException from "../../Types/Exception/ServerException";
2
+ import {
3
+ ExpressRequest,
4
+ ExpressResponse,
5
+ NextFunction,
6
+ OneUptimeRequest,
7
+ headerValueToString,
8
+ } from "../Utils/Express";
9
+ import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
10
+ import Response from "../Utils/Response";
11
+ import zlib from "zlib";
12
+
13
+ /*
14
+ * Ceilings for the global `Content-Encoding: gzip` request path.
15
+ *
16
+ * Both are 50 MiB, which is exactly what body-parser is given for the
17
+ * identity path (`jsonBodyParserOptions.limit` / `urlEncodedBodyParserOptions.limit`,
18
+ * and `bytes("50mb")` is 1024-based too). Announcing gzip must not buy a
19
+ * caller more room than sending the same body uncompressed.
20
+ *
21
+ * They are two constants because they bound two different things and
22
+ * neither alone is sufficient:
23
+ *
24
+ * - COMPRESSED bounds what arrives on the socket, so a client cannot
25
+ * stream unbounded bytes into the process.
26
+ * - DECOMPRESSED bounds what the inflate produces. gzip of repeated
27
+ * bytes reaches four figures of amplification: 130,479 bytes of gzip
28
+ * inflates to 134,217,728 bytes (1,029x, measured), so the compressed
29
+ * cap on its own leaves ~50 GB of expansion on the table.
30
+ */
31
+ export const MAX_COMPRESSED_REQUEST_BODY_BYTES: number = 50 * 1024 * 1024;
32
+ export const MAX_DECOMPRESSED_REQUEST_BODY_BYTES: number = 50 * 1024 * 1024;
33
+
34
+ /*
35
+ * Inflate a `Content-Encoding: gzip` request body under hard limits, and
36
+ * hand the decompressed bytes on as `req.body` (a Buffer, which is what
37
+ * this path has always produced).
38
+ *
39
+ * This middleware runs BEFORE routing and therefore before authentication,
40
+ * so every limit here is an unauthenticated-attacker limit.
41
+ *
42
+ * Two implementation notes that are easy to get wrong:
43
+ *
44
+ * 1. It streams. Buffering the whole compressed body and then calling
45
+ * `zlib.gunzip` would hold the compressed cap AND the decompressed cap
46
+ * at once, and would not notice a bomb until the last byte arrived.
47
+ * Feeding a `createGunzip` stream instead trips the cap after ~64 KiB
48
+ * of a 128 MiB bomb (measured), before most of the body is even sent.
49
+ *
50
+ * 2. It counts the output itself. `maxOutputLength` looks like the right
51
+ * knob but Node only honours it on the convenience methods
52
+ * (`zlib.gunzip` / `gunzipSync`); on a `createGunzip` STREAM it is
53
+ * silently ignored - a stream with `maxOutputLength: 1 MiB` happily
54
+ * emits 128 MiB. Verified against Node 24. Setting it here would read
55
+ * as protection while providing none, so the running total below is
56
+ * what actually enforces the ceiling.
57
+ */
58
+ export default class GzipRequestBodyMiddleware {
59
+ public static parseBody(
60
+ req: ExpressRequest,
61
+ res: ExpressResponse,
62
+ next: NextFunction,
63
+ ): void {
64
+ /*
65
+ * Cheap pre-check before a byte is read. Content-Length is advisory (a
66
+ * chunked request carries none) but when it is present and already over
67
+ * the cap there is no reason to accept the body at all.
68
+ */
69
+ const contentLengthHeader: string | undefined = headerValueToString(
70
+ req.headers["content-length"],
71
+ );
72
+
73
+ if (contentLengthHeader) {
74
+ const declaredLength: number = parseInt(contentLengthHeader, 10);
75
+
76
+ if (
77
+ !isNaN(declaredLength) &&
78
+ declaredLength > MAX_COMPRESSED_REQUEST_BODY_BYTES
79
+ ) {
80
+ GzipRequestBodyMiddleware.rejectTooLarge(
81
+ req,
82
+ res,
83
+ "compressed",
84
+ declaredLength,
85
+ MAX_COMPRESSED_REQUEST_BODY_BYTES,
86
+ );
87
+ return;
88
+ }
89
+ }
90
+
91
+ const inflate: zlib.Gunzip = zlib.createGunzip();
92
+
93
+ const decoded: Array<Buffer> = [];
94
+
95
+ let compressedBytes: number = 0;
96
+ let decompressedBytes: number = 0;
97
+ let requestEnded: boolean = false;
98
+ let awaitingDrain: boolean = false;
99
+ let settled: boolean = false;
100
+
101
+ const settle: (finish: () => void) => void = (finish: () => void): void => {
102
+ if (settled) {
103
+ return;
104
+ }
105
+
106
+ settled = true;
107
+
108
+ req.removeListener("data", onData);
109
+ req.removeListener("end", onEnd);
110
+ req.removeListener("error", onRequestError);
111
+ req.removeListener("close", onClose);
112
+
113
+ inflate.removeAllListeners();
114
+
115
+ /*
116
+ * A stream that emits "error" with no listener throws, and an
117
+ * uncaught throw here would turn a memory-exhaustion fix into a
118
+ * process-exit one. Nothing can consume a post-teardown error, so
119
+ * swallow it explicitly rather than leaving the stream bare.
120
+ */
121
+ inflate.on("error", (): void => {});
122
+ inflate.destroy();
123
+
124
+ decoded.length = 0;
125
+
126
+ finish();
127
+ };
128
+
129
+ /*
130
+ * Stop reading rather than draining. The session replay middleware
131
+ * deliberately drains its oversized bodies to keep the keep-alive
132
+ * connection usable for the next chunk, but that is a cooperative
133
+ * first-party client sending a 4 MiB cap's worth of bytes. Here the
134
+ * body we are refusing may be gigabytes from someone who wants us to
135
+ * read them, so we pause: Node flushes the response we already wrote
136
+ * and then closes the connection because the request was never
137
+ * consumed, which is precisely the outcome we want.
138
+ */
139
+ const stopReading: () => void = (): void => {
140
+ if (typeof req.pause === "function") {
141
+ req.pause();
142
+ }
143
+ };
144
+
145
+ const onData: (chunk: Buffer | string) => void = (
146
+ chunk: Buffer | string,
147
+ ): void => {
148
+ const asBuffer: Buffer = Buffer.isBuffer(chunk)
149
+ ? chunk
150
+ : Buffer.from(chunk, "utf-8");
151
+
152
+ compressedBytes += asBuffer.length;
153
+
154
+ if (compressedBytes > MAX_COMPRESSED_REQUEST_BODY_BYTES) {
155
+ settle((): void => {
156
+ stopReading();
157
+ GzipRequestBodyMiddleware.rejectTooLarge(
158
+ req,
159
+ res,
160
+ "compressed",
161
+ compressedBytes,
162
+ MAX_COMPRESSED_REQUEST_BODY_BYTES,
163
+ );
164
+ });
165
+ return;
166
+ }
167
+
168
+ /*
169
+ * Honour the inflate's backpressure. zlib does its work on the
170
+ * threadpool, so a socket that is faster than the inflate will
171
+ * otherwise pile the whole compressed body into the writable queue -
172
+ * which is the unbounded buffering this middleware exists to remove,
173
+ * just one object further down. Pausing also means the caps below get
174
+ * to see the output before we accept the next chunk, so a bomb is
175
+ * refused while most of it is still on the wire.
176
+ */
177
+ if (!inflate.write(asBuffer) && !awaitingDrain) {
178
+ awaitingDrain = true;
179
+
180
+ if (typeof req.pause === "function") {
181
+ req.pause();
182
+ }
183
+
184
+ inflate.once("drain", (): void => {
185
+ awaitingDrain = false;
186
+
187
+ if (!settled && typeof req.resume === "function") {
188
+ req.resume();
189
+ }
190
+ });
191
+ }
192
+ };
193
+
194
+ const onEnd: () => void = (): void => {
195
+ requestEnded = true;
196
+
197
+ /*
198
+ * A request that announced gzip and then sent nothing. `inflate.end()`
199
+ * on an empty stream raises "unexpected end of file", which used to
200
+ * turn every such request - including a bare GET carrying the header -
201
+ * into a 500 plus an error-level log line. Treat it as the empty body
202
+ * it is and let the route decide.
203
+ */
204
+ if (compressedBytes === 0) {
205
+ settle((): void => {
206
+ req.body = Buffer.alloc(0);
207
+ next();
208
+ });
209
+ return;
210
+ }
211
+
212
+ inflate.end();
213
+ };
214
+
215
+ const onRequestError: (err: Error) => void = (err: Error): void => {
216
+ settle((): void => {
217
+ next(err);
218
+ });
219
+ };
220
+
221
+ /*
222
+ * The client hung up mid-body. There is nobody left to answer, so drop
223
+ * the inflate and everything it is holding instead of waiting for a
224
+ * `end` that will never come. `close` also fires on the happy path,
225
+ * after `end`, which is what `requestEnded` guards.
226
+ */
227
+ const onClose: () => void = (): void => {
228
+ if (requestEnded) {
229
+ return;
230
+ }
231
+
232
+ settle((): void => {});
233
+ };
234
+
235
+ inflate.on("data", (chunk: Buffer): void => {
236
+ decompressedBytes += chunk.length;
237
+
238
+ if (decompressedBytes > MAX_DECOMPRESSED_REQUEST_BODY_BYTES) {
239
+ settle((): void => {
240
+ stopReading();
241
+ GzipRequestBodyMiddleware.rejectTooLarge(
242
+ req,
243
+ res,
244
+ "decompressed",
245
+ decompressedBytes,
246
+ MAX_DECOMPRESSED_REQUEST_BODY_BYTES,
247
+ );
248
+ });
249
+ return;
250
+ }
251
+
252
+ decoded.push(chunk);
253
+ });
254
+
255
+ inflate.on("end", (): void => {
256
+ const body: Buffer = Buffer.concat(
257
+ decoded as unknown as Array<Uint8Array>,
258
+ );
259
+
260
+ settle((): void => {
261
+ req.body = body;
262
+ next();
263
+ });
264
+ });
265
+
266
+ inflate.on("error", (err: Error): void => {
267
+ settle((): void => {
268
+ logger.error(err, getLogAttributesFromRequest(req as OneUptimeRequest));
269
+
270
+ Response.sendErrorResponse(
271
+ req,
272
+ res,
273
+ new ServerException("Error decompressing data"),
274
+ );
275
+ });
276
+ });
277
+
278
+ req.on("data", onData);
279
+ req.on("end", onEnd);
280
+ req.on("error", onRequestError);
281
+ req.on("close", onClose);
282
+ }
283
+
284
+ private static rejectTooLarge(
285
+ req: ExpressRequest,
286
+ res: ExpressResponse,
287
+ stage: "compressed" | "decompressed",
288
+ bytes: number,
289
+ limit: number,
290
+ ): void {
291
+ if (res.headersSent) {
292
+ return;
293
+ }
294
+
295
+ /*
296
+ * debug, not warn or error. This path is unauthenticated, so a level
297
+ * that reaches the log sink by default would hand the same attacker a
298
+ * log-amplification vector in place of the memory one - while still
299
+ * leaving an operator something to turn on when a legitimate client
300
+ * starts getting 413s.
301
+ */
302
+ logger.debug(
303
+ `Rejected a gzip request body: ${stage} size reached ${bytes} bytes, limit is ${limit}.`,
304
+ getLogAttributesFromRequest(req as OneUptimeRequest),
305
+ );
306
+
307
+ res.status(413).json({
308
+ message: `Request body too large. The ${stage} body of a gzip-encoded request may not exceed ${limit} bytes.`,
309
+ });
310
+ }
311
+ }
@@ -1,17 +1,101 @@
1
+ import BadRequestException from "../../Types/Exception/BadRequestException";
2
+ import PayloadTooLargeException from "../../Types/Exception/PayloadTooLargeException";
3
+ import {
4
+ ExpressRequest,
5
+ ExpressResponse,
6
+ NextFunction,
7
+ } from "../Utils/Express";
1
8
  import multer from "multer";
2
9
  import { RequestHandler } from "express";
3
10
 
11
+ /*
12
+ * Ceilings for multipart bodies.
13
+ *
14
+ * memoryStorage means every part is buffered in the process, and both
15
+ * routes that mount this middleware run it BEFORE their auth check -
16
+ * Pyroscope ingest before isAuthorizedServiceMiddleware, SendGrid inbound
17
+ * before the webhook secret is verified. So these are the limits an
18
+ * unauthenticated caller is held to, and multer's defaults leave most of
19
+ * them at Infinity: file size, file count, field count and part count are
20
+ * all unbounded out of the box (only fieldSize has a default, 1 MB).
21
+ *
22
+ * The sizes match the 50 MiB body-parser limit the rest of the app uses,
23
+ * and nginx cuts the shipped deployment lower still (50M on
24
+ * /incoming-email, its 1M default on /pyroscope). The COUNTS are the part
25
+ * that was genuinely missing: without them a body of ten thousand
26
+ * one-byte parts costs ten thousand allocations and passes every size
27
+ * check.
28
+ */
29
+ export const MAX_MULTIPART_FILE_BYTES: number = 50 * 1024 * 1024;
30
+ export const MAX_MULTIPART_FIELD_BYTES: number = 25 * 1024 * 1024;
31
+ export const MAX_MULTIPART_FILES: number = 50;
32
+ export const MAX_MULTIPART_FIELDS: number = 200;
33
+
4
34
  /*
5
35
  * Configure multer for handling multipart/form-data
6
36
  * Uses memory storage to store files in memory as Buffer objects
7
37
  */
8
- const upload: multer.Multer = multer({ storage: multer.memoryStorage() });
38
+ const upload: multer.Multer = multer({
39
+ storage: multer.memoryStorage(),
40
+ limits: {
41
+ fileSize: MAX_MULTIPART_FILE_BYTES,
42
+ fieldSize: MAX_MULTIPART_FIELD_BYTES,
43
+ files: MAX_MULTIPART_FILES,
44
+ fields: MAX_MULTIPART_FIELDS,
45
+ /*
46
+ * Parts are files + fields, so this has to allow both in full or it
47
+ * would be the effective limit and the two above would never fire.
48
+ */
49
+ parts: MAX_MULTIPART_FILES + MAX_MULTIPART_FIELDS,
50
+ },
51
+ });
52
+
53
+ const uploadAny: RequestHandler = upload.any() as unknown as RequestHandler;
54
+
55
+ /*
56
+ * The LIMIT_* codes that mean "you sent too much". LIMIT_UNEXPECTED_FILE
57
+ * is deliberately absent: it means the body named a field the route did
58
+ * not ask for, which is a malformed request rather than an oversized one.
59
+ * (`.any()` accepts every field name, so it should not arise here - the
60
+ * distinction is kept so this stays correct if the mount ever narrows.)
61
+ */
62
+ const TOO_LARGE_CODES: Set<string> = new Set<string>([
63
+ "LIMIT_PART_COUNT",
64
+ "LIMIT_FILE_SIZE",
65
+ "LIMIT_FILE_COUNT",
66
+ "LIMIT_FIELD_KEY",
67
+ "LIMIT_FIELD_VALUE",
68
+ "LIMIT_FIELD_COUNT",
69
+ ]);
9
70
 
10
71
  /*
11
72
  * Middleware for handling any file uploads (multipart/form-data)
12
73
  * This is useful for webhooks that send data as multipart/form-data (e.g., SendGrid inbound email)
74
+ *
75
+ * Wrapped so a limit breach answers 413 rather than 500. multer signals
76
+ * every limit with a MulterError whose `code` is one of the LIMIT_*
77
+ * values, and nothing downstream knows what those mean - the generic
78
+ * error handler would turn a caller's oversized upload into a server
79
+ * error and page whoever is on call for it.
13
80
  */
14
- const MultipartFormDataMiddleware: RequestHandler =
15
- upload.any() as unknown as RequestHandler;
81
+ const MultipartFormDataMiddleware: RequestHandler = (
82
+ req: ExpressRequest,
83
+ res: ExpressResponse,
84
+ next: NextFunction,
85
+ ): void => {
86
+ uploadAny(req, res, (err: unknown): void => {
87
+ if (err instanceof multer.MulterError) {
88
+ const message: string = `Multipart request rejected: ${err.code}.`;
89
+
90
+ return next(
91
+ TOO_LARGE_CODES.has(err.code)
92
+ ? new PayloadTooLargeException(message)
93
+ : new BadRequestException(message),
94
+ );
95
+ }
96
+
97
+ return next(err);
98
+ });
99
+ };
16
100
 
17
101
  export default MultipartFormDataMiddleware;
@@ -4,6 +4,10 @@ import CreateBy from "../Types/Database/CreateBy";
4
4
  import UpdateBy from "../Types/Database/UpdateBy";
5
5
  import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
6
6
  import BadDataException from "../../Types/Exception/BadDataException";
7
+ import {
8
+ DETECTION_MATCH_COUNT_THRESHOLD_MAX,
9
+ DETECTION_MATCH_COUNT_THRESHOLD_MIN,
10
+ } from "../../Types/SecurityEvent/DetectionFindingConstants";
7
11
  import SigmaRuleParser from "../../Utils/SecurityEvent/Sigma/SigmaRuleParser";
8
12
  import SigmaClickhouseCompiler from "../Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler";
9
13
 
@@ -25,23 +29,69 @@ export class Service extends DatabaseService<Model> {
25
29
  SigmaClickhouseCompiler.compileYaml(ruleYaml);
26
30
  }
27
31
 
28
- private validateEvaluationInterval(interval: number | undefined): void {
29
- if (interval === undefined) {
32
+ // undefined always passes: an omitted field means "keep the default".
33
+ private validateIntegerInRange(data: {
34
+ value: number | undefined;
35
+ min: number;
36
+ max: number;
37
+ message: string;
38
+ }): void {
39
+ if (data.value === undefined) {
30
40
  return;
31
41
  }
32
42
 
33
- if (!Number.isInteger(interval) || interval < 1 || interval > 1440) {
34
- throw new BadDataException(
35
- "Evaluation interval must be a whole number of minutes between 1 and 1440.",
36
- );
43
+ if (
44
+ !Number.isInteger(data.value) ||
45
+ data.value < data.min ||
46
+ data.value > data.max
47
+ ) {
48
+ throw new BadDataException(data.message);
37
49
  }
38
50
  }
39
51
 
52
+ private validateEvaluationInterval(interval: number | undefined): void {
53
+ this.validateIntegerInRange({
54
+ value: interval,
55
+ min: 1,
56
+ max: 1440,
57
+ message:
58
+ "Evaluation interval must be a whole number of minutes between 1 and 1440.",
59
+ });
60
+ }
61
+
62
+ private validateMatchCountThreshold(threshold: number | undefined): void {
63
+ this.validateIntegerInRange({
64
+ value: threshold,
65
+ min: DETECTION_MATCH_COUNT_THRESHOLD_MIN,
66
+ max: DETECTION_MATCH_COUNT_THRESHOLD_MAX,
67
+ message: `Match count threshold must be a whole number between ${DETECTION_MATCH_COUNT_THRESHOLD_MIN} and ${DETECTION_MATCH_COUNT_THRESHOLD_MAX}.`,
68
+ });
69
+ }
70
+
40
71
  protected override async onBeforeCreate(
41
72
  createBy: CreateBy<Model>,
42
73
  ): Promise<OnCreate<Model>> {
43
74
  this.validateSigmaRule(createBy.data.sigmaRuleYaml);
44
75
  this.validateEvaluationInterval(createBy.data.evaluationIntervalInMinutes);
76
+ this.validateMatchCountThreshold(createBy.data.matchCountThreshold);
77
+
78
+ /*
79
+ * groupByField / distinctCountField are looked up verbatim: an
80
+ * unknown name silently becomes an attributes[] lookup that yields
81
+ * '' on every row, and for a distinct-count rule that means it never
82
+ * fires again with no error anywhere. Trimming here kills the whole
83
+ * whitespace-from-a-paste class of that outage; '' is preserved as
84
+ * the documented "feature off" value so clearing the field still
85
+ * works.
86
+ */
87
+ if (createBy.data.groupByField !== undefined) {
88
+ createBy.data.groupByField = createBy.data.groupByField.trim();
89
+ }
90
+
91
+ if (createBy.data.distinctCountField !== undefined) {
92
+ createBy.data.distinctCountField =
93
+ createBy.data.distinctCountField.trim();
94
+ }
45
95
 
46
96
  /*
47
97
  * Default the rule name from the Sigma title so pasting a rule is a
@@ -67,6 +117,23 @@ export class Service extends DatabaseService<Model> {
67
117
  updateBy.data.evaluationIntervalInMinutes as number | undefined,
68
118
  );
69
119
 
120
+ this.validateMatchCountThreshold(
121
+ updateBy.data.matchCountThreshold as number | undefined,
122
+ );
123
+
124
+ // Same trim as onBeforeCreate — see the rationale there.
125
+ if (updateBy.data.groupByField !== undefined) {
126
+ updateBy.data.groupByField = (
127
+ updateBy.data.groupByField as string
128
+ ).trim();
129
+ }
130
+
131
+ if (updateBy.data.distinctCountField !== undefined) {
132
+ updateBy.data.distinctCountField = (
133
+ updateBy.data.distinctCountField as string
134
+ ).trim();
135
+ }
136
+
70
137
  return { updateBy, carryForward: null };
71
138
  }
72
139
  }