@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.
- package/Models/DatabaseModels/DetectionRule.ts +55 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +584 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +105 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.ts +60 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +207 -0
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +917 -0
- package/Server/Services/NetworkDeviceAutoImportRuleService.ts +181 -0
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
- package/Server/Services/NetworkDeviceService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
- package/Server/Services/ProjectService.ts +29 -19
- package/Server/Services/SecurityEventService.ts +90 -13
- package/Server/Services/StatusPageResourceService.ts +223 -0
- package/Server/Services/TelemetryAttributeService.ts +17 -0
- package/Server/Services/UserService.ts +4 -6
- package/Server/Types/Markdown.ts +161 -67
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
- package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
- package/Server/Utils/Attribution.ts +4 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +39 -1
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
- package/Server/Utils/StartServer.ts +12 -32
- package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
- package/Server/Utils/Telemetry/LlmMetricSpend.ts +130 -0
- package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
- package/Server/Views/Partials/Head.ejs +2 -1
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
- package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
- package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
- package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
- package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
- package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
- package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
- package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
- package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
- package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
- package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
- package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
- package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
- package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
- package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
- package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
- package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
- package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
- package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
- package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +205 -0
- package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +933 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
- package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
- package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
- package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
- package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
- package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +97 -1
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
- package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
- package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +368 -4
- package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -0
- package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
- package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
- package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
- package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
- package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +193 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
- package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
- package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
- package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
- package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
- package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
- package/Tests/UI/Components/SideOver.test.tsx +1 -1
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
- package/Tests/UI/Components/Toggle.test.tsx +25 -0
- package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
- package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +516 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +272 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +164 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -0
- package/Types/Code/YamlSyntax.ts +396 -0
- package/Types/Exception/ExceptionCode.ts +1 -0
- package/Types/Exception/PayloadTooLargeException.ts +8 -0
- package/Types/Marketing/Attribution.ts +10 -0
- package/Types/Marketing/MarketingEvent.ts +4 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
- package/Types/NetworkAutomation/RuleRunResult.ts +84 -0
- package/Types/Permission.ts +47 -0
- package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
- package/Types/Telemetry/LlmMetricConventions.ts +124 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
- package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
- package/UI/Components/Detail/Detail.tsx +10 -0
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
- package/UI/Components/Forms/Fields/FormField.tsx +31 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
- package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
- package/UI/Components/Forms/Validation.ts +46 -0
- package/UI/Components/Input/Input.tsx +3 -0
- package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
- package/UI/Components/ModelTable/Column.ts +8 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
- package/UI/Components/SideOver/SideOver.tsx +5 -5
- package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
- package/UI/Components/StatusPage/ResourceList.tsx +197 -38
- package/UI/Components/Toggle/Toggle.tsx +9 -1
- package/UI/Components/Types/FieldType.ts +1 -0
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
- package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +257 -0
- package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +183 -0
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +95 -0
- package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +66 -0
- package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +82 -0
- package/Utils/StatusPage/ResourceExplorer.ts +258 -0
- package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +609 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +97 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +76 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +76 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js +35 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
- package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
- package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +206 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +711 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +155 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -45
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventService.js +57 -5
- package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
- package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +2 -12
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +147 -61
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Attribution.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +26 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -21
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +102 -0
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
- package/build/dist/Types/Code/YamlSyntax.js +212 -0
- package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
- package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
- package/build/dist/Types/Marketing/Attribution.js +10 -0
- package/build/dist/Types/Marketing/Attribution.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +24 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/Permission.js +41 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmMetricConventions.js +98 -0
- package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +9 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +27 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
- package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Types/FieldType.js +1 -0
- package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +149 -0
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +121 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +75 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +59 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +60 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js +119 -0
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +2 -1
- package/Tests/UI/Components/Toast.test.tsx +0 -197
- package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
- package/UI/Components/Toast/Toast.tsx +0 -189
- package/UI/Components/Toast/ToastInit.tsx +0 -110
- package/build/dist/UI/Components/Toast/Toast.js +0 -123
- package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
- 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({
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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 (
|
|
34
|
-
|
|
35
|
-
|
|
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
|
}
|