@oneuptime/common 12.0.30 → 12.0.32

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 (645) hide show
  1. package/Models/AnalyticsModels/SecurityEvent.ts +48 -16
  2. package/Models/AnalyticsModels/ThreatIntelIndicator.ts +10 -5
  3. package/Models/DatabaseModels/AlertSeverity.ts +44 -0
  4. package/Models/DatabaseModels/AlertState.ts +0 -2
  5. package/Models/DatabaseModels/DetectionRule.ts +16 -5
  6. package/Models/DatabaseModels/Domain.ts +1 -1
  7. package/Models/DatabaseModels/GoogleSecOpsConnection.ts +14 -5
  8. package/Models/DatabaseModels/Incident.ts +1 -1
  9. package/Models/DatabaseModels/IncidentSeverity.ts +44 -0
  10. package/Models/DatabaseModels/IncidentTemplate.ts +1 -1
  11. package/Models/DatabaseModels/Index.ts +4 -0
  12. package/Models/DatabaseModels/MetricType.ts +0 -2
  13. package/Models/DatabaseModels/MonitorStatusTimeline.ts +0 -2
  14. package/Models/DatabaseModels/MonitorTest.ts +0 -2
  15. package/Models/DatabaseModels/NetworkDevice.ts +234 -5
  16. package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +88 -0
  17. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +22 -0
  18. package/Models/DatabaseModels/NetworkDeviceOidTemplate.ts +501 -0
  19. package/Models/DatabaseModels/NetworkDeviceRole.ts +648 -0
  20. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -1
  21. package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -1
  22. package/Models/DatabaseModels/StatusPageAnnouncement.ts +0 -2
  23. package/Models/DatabaseModels/StatusPageAnnouncementTemplate.ts +0 -2
  24. package/Models/DatabaseModels/StatusPagePrivateUser.ts +0 -2
  25. package/Models/DatabaseModels/StatusPageResource.ts +0 -2
  26. package/Models/DatabaseModels/StatusPageSubscriber.ts +0 -2
  27. package/Models/DatabaseModels/ThreatIntelFeed.ts +16 -5
  28. package/Server/API/GlobalConfigAPI.ts +362 -183
  29. package/Server/API/TelemetryAPI.ts +42 -4
  30. package/Server/EnvironmentConfig.ts +112 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.ts +59 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.ts +25 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.ts +72 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  35. package/Server/Middleware/MultipartFormData.ts +118 -32
  36. package/Server/Services/AlertEpisodeLabelRuleEngineService.ts +9 -1
  37. package/Server/Services/AlertEpisodeOnCallRuleEngineService.ts +9 -1
  38. package/Server/Services/AlertEpisodeOwnerRuleEngineService.ts +9 -1
  39. package/Server/Services/AlertEpisodePrivacyRuleEngineService.ts +9 -1
  40. package/Server/Services/AlertGroupingEngineService.ts +9 -1
  41. package/Server/Services/AlertLabelRuleEngineService.ts +9 -1
  42. package/Server/Services/AlertOnCallRuleEngineService.ts +9 -1
  43. package/Server/Services/AlertOwnerRuleEngineService.ts +9 -1
  44. package/Server/Services/AlertPrivacyRuleEngineService.ts +9 -1
  45. package/Server/Services/AlertReminderRuleService.ts +9 -1
  46. package/Server/Services/AutoRemediationRuleEngineService.ts +9 -1
  47. package/Server/Services/CephClusterLabelRuleEngineService.ts +9 -1
  48. package/Server/Services/CephClusterOwnerRuleEngineService.ts +9 -1
  49. package/Server/Services/CloudResourceLabelRuleEngineService.ts +9 -1
  50. package/Server/Services/CloudResourceOwnerRuleEngineService.ts +9 -1
  51. package/Server/Services/DashboardLabelRuleEngineService.ts +9 -1
  52. package/Server/Services/DashboardOwnerRuleEngineService.ts +9 -1
  53. package/Server/Services/DatabaseService.ts +39 -9
  54. package/Server/Services/DockerHostLabelRuleEngineService.ts +9 -1
  55. package/Server/Services/DockerHostOwnerRuleEngineService.ts +9 -1
  56. package/Server/Services/DockerSwarmClusterLabelRuleEngineService.ts +9 -1
  57. package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +9 -1
  58. package/Server/Services/HostLabelRuleEngineService.ts +9 -1
  59. package/Server/Services/HostOwnerRuleEngineService.ts +9 -1
  60. package/Server/Services/IncidentEpisodeLabelRuleEngineService.ts +9 -1
  61. package/Server/Services/IncidentEpisodeOnCallRuleEngineService.ts +9 -1
  62. package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +9 -1
  63. package/Server/Services/IncidentEpisodePrivacyRuleEngineService.ts +9 -1
  64. package/Server/Services/IncidentGroupingEngineService.ts +9 -1
  65. package/Server/Services/IncidentLabelRuleEngineService.ts +9 -1
  66. package/Server/Services/IncidentOnCallRuleEngineService.ts +9 -1
  67. package/Server/Services/IncidentOwnerRuleEngineService.ts +9 -1
  68. package/Server/Services/IncidentPrivacyRuleEngineService.ts +9 -1
  69. package/Server/Services/IncidentReminderRuleService.ts +9 -1
  70. package/Server/Services/IncidentSlaRuleService.ts +9 -1
  71. package/Server/Services/IncomingCallPolicyLabelRuleEngineService.ts +9 -1
  72. package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +9 -1
  73. package/Server/Services/Index.ts +4 -0
  74. package/Server/Services/IoTFleetLabelRuleEngineService.ts +9 -1
  75. package/Server/Services/IoTFleetOwnerRuleEngineService.ts +9 -1
  76. package/Server/Services/KubernetesClusterLabelRuleEngineService.ts +9 -1
  77. package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +9 -1
  78. package/Server/Services/KubernetesResourceService.ts +5 -1
  79. package/Server/Services/MonitorLabelRuleEngineService.ts +9 -1
  80. package/Server/Services/MonitorOwnerRuleEngineService.ts +9 -1
  81. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +35 -0
  82. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +96 -0
  83. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +9 -1
  84. package/Server/Services/NetworkDeviceOidTemplateService.ts +201 -0
  85. package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +9 -1
  86. package/Server/Services/NetworkDeviceRoleService.ts +90 -0
  87. package/Server/Services/NetworkDeviceService.ts +209 -1
  88. package/Server/Services/OnCallDutyPolicyLabelRuleEngineService.ts +9 -1
  89. package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +9 -1
  90. package/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.ts +9 -1
  91. package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +9 -1
  92. package/Server/Services/PodmanHostLabelRuleEngineService.ts +9 -1
  93. package/Server/Services/PodmanHostOwnerRuleEngineService.ts +9 -1
  94. package/Server/Services/ProjectService.ts +85 -0
  95. package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +9 -1
  96. package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +9 -1
  97. package/Server/Services/RumApplicationLabelRuleEngineService.ts +9 -1
  98. package/Server/Services/RumApplicationOwnerRuleEngineService.ts +9 -1
  99. package/Server/Services/RunbookLabelRuleEngineService.ts +9 -1
  100. package/Server/Services/RunbookOwnerRuleEngineService.ts +9 -1
  101. package/Server/Services/RunbookRuleEngineService.ts +9 -1
  102. package/Server/Services/ScheduledMaintenanceLabelRuleEngineService.ts +9 -1
  103. package/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.ts +9 -1
  104. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +9 -1
  105. package/Server/Services/ServerlessFunctionLabelRuleEngineService.ts +9 -1
  106. package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +9 -1
  107. package/Server/Services/ServiceLabelRuleEngineService.ts +9 -1
  108. package/Server/Services/ServiceOwnerRuleEngineService.ts +9 -1
  109. package/Server/Services/StatusPageLabelRuleEngineService.ts +9 -1
  110. package/Server/Services/StatusPageMonitorRuleEngineService.ts +7 -0
  111. package/Server/Services/StatusPageOwnerRuleEngineService.ts +9 -1
  112. package/Server/Services/TelemetrySourceMapService.ts +266 -23
  113. package/Server/Services/UserNotificationSettingService.ts +0 -6
  114. package/Server/Services/UserService.ts +27 -0
  115. package/Server/Services/WorkflowLabelRuleEngineService.ts +9 -1
  116. package/Server/Services/WorkflowOwnerRuleEngineService.ts +9 -1
  117. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +186 -0
  118. package/Server/Utils/Monitor/CapturedMetricAttributeUtil.ts +245 -0
  119. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +21 -2
  120. package/Server/Utils/Monitor/MonitorMetricUtil.ts +55 -21
  121. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +32 -0
  122. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +15 -2
  123. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +18 -1
  124. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +10 -0
  125. package/Server/Utils/Monitor/SeriesResourceLabels.ts +34 -0
  126. package/Server/Utils/Rules/RuleEngineRuleRead.ts +48 -0
  127. package/Server/Utils/Telemetry/ExceptionSanitizer.ts +14 -1
  128. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +14 -2
  129. package/Server/Utils/Telemetry/SourceMapResolver.ts +16 -5
  130. package/Server/Utils/Telemetry/StackTraceParser.ts +399 -0
  131. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +31 -1
  132. package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +35 -8
  133. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +107 -3
  134. package/Tests/App/Dashboard/DashboardWideChartZoom.test.tsx +401 -0
  135. package/Tests/App/Dashboard/EventOverlayHook.test.tsx +125 -0
  136. package/Tests/App/Dashboard/HeaderImageAssets.test.tsx +93 -0
  137. package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +318 -0
  138. package/Tests/App/Dashboard/NetworkDeviceBulkDelete.test.tsx +357 -0
  139. package/Tests/App/Dashboard/NetworkSideMenu.test.tsx +590 -0
  140. package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3 -3
  141. package/Tests/App/Dashboard/SideMenuHarness.tsx +11 -3
  142. package/Tests/App/Dashboard/UserElementEmail.test.tsx +12 -4
  143. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +1 -1
  144. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +14 -0
  145. package/Tests/Models/DatabaseModels/ModelRegistryInvariants.test.ts +543 -0
  146. package/Tests/Models/SecurityDomainAccessControl.test.ts +429 -0
  147. package/Tests/Server/API/GlobalConfigLicense.test.ts +657 -0
  148. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +84 -6
  149. package/Tests/Server/API/TelemetryResolveStackTraceAPI.test.ts +805 -0
  150. package/Tests/Server/Infrastructure/Postgres/AddOnCallCalendarFeedsMigration.test.ts +32 -7
  151. package/Tests/Server/Infrastructure/Postgres/NetworkDeviceRoleTableMigration.test.ts +476 -0
  152. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +1144 -0
  153. package/Tests/Server/Middleware/MultipartFormData.test.ts +618 -3
  154. package/Tests/Server/Services/DatabaseServiceGenerateSlug.test.ts +215 -0
  155. package/Tests/Server/Services/MonitorLabelRuleEngineScale.test.ts +520 -0
  156. package/Tests/Server/Services/NetworkDeviceOidTemplateService.test.ts +332 -0
  157. package/Tests/Server/Services/NetworkDeviceOidTemplateWriteGuards.test.ts +409 -0
  158. package/Tests/Server/Services/NetworkDeviceRoleModel.test.ts +672 -0
  159. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +1 -1
  160. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +67 -4
  161. package/Tests/Server/Services/RuleEngineRuleFetchLimit.test.ts +239 -0
  162. package/Tests/Server/Services/ScheduledMaintenanceSubscriberEndTime.test.ts +369 -0
  163. package/Tests/Server/Services/TelemetrySourceMapService.test.ts +1210 -2
  164. package/Tests/Server/Services/UserNotificationSettingMonitorDefaults.test.ts +248 -0
  165. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +43 -2
  166. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +182 -0
  167. package/Tests/Server/Types/AnalyticsDatabase/SecurityEventModelPermission.test.ts +255 -0
  168. package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +212 -0
  169. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +413 -0
  170. package/Tests/Server/Utils/BasicCron.test.ts +283 -0
  171. package/Tests/Server/Utils/CodeRepository.test.ts +689 -0
  172. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +495 -0
  173. package/Tests/Server/Utils/ExceptionSanitizer.test.ts +77 -0
  174. package/Tests/Server/Utils/IncomingCallPhoneNumber.test.ts +174 -0
  175. package/Tests/Server/Utils/Monitor/CapturedMetricAttributeUtil.test.ts +284 -0
  176. package/Tests/Server/Utils/Monitor/MonitorCustomMetricAttributes.test.ts +503 -0
  177. package/Tests/Server/Utils/Monitor/MonitorTemplatePrototypePollution.test.ts +106 -0
  178. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkLogWritePath.test.ts +33 -4
  179. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +14 -4
  180. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +44 -0
  181. package/Tests/Server/Utils/OpenAPISpec.test.ts +536 -0
  182. package/Tests/Server/Utils/Rules/RuleEngineRuleRead.test.ts +115 -0
  183. package/Tests/Server/Utils/StackTraceParser.test.ts +610 -0
  184. package/Tests/Server/Utils/Telemetry/LogExceptionExtractor.test.ts +0 -0
  185. package/Tests/Server/Utils/Telemetry/SourceMapLimits.test.ts +656 -0
  186. package/Tests/Server/Utils/Telemetry/SourceMapResolver.test.ts +100 -0
  187. package/Tests/Types/Monitor/SnmpMonitor/SnmpOidListUtil.test.ts +454 -0
  188. package/Tests/Types/NetworkDevice/DefaultNetworkDeviceRole.test.ts +417 -0
  189. package/Tests/Types/SecurityRolePermissions.test.ts +193 -0
  190. package/Tests/UI/Components/Charts/AnnotationLayout.test.ts +773 -0
  191. package/Tests/UI/Components/Charts/ChartAnnotationRail.test.tsx +789 -0
  192. package/Tests/UI/Components/Charts/ChartBucketIdentity.test.ts +254 -0
  193. package/Tests/UI/Components/Charts/ChartClickDoubleClickDisambiguation.test.tsx +221 -0
  194. package/Tests/UI/Components/Charts/ChartDoubleClickReset.test.tsx +220 -0
  195. package/Tests/UI/Components/Charts/TimeAnnotation.test.ts +471 -0
  196. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +405 -0
  197. package/Tests/UI/Components/ErrorMessageRefreshControl.test.tsx +83 -0
  198. package/Tests/UI/Components/MarkdownConverters.test.ts +686 -0
  199. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +751 -0
  200. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +475 -43
  201. package/Tests/UI/Components/ModelTable/BaseModelTableDeleteAndEmptyStates.test.tsx +341 -0
  202. package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +89 -0
  203. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +103 -8
  204. package/Tests/UI/Components/OrderedStatesList.test.tsx +5 -5
  205. package/Tests/UI/Components/PendingProjectInvitations.test.tsx +1 -1
  206. package/Tests/UI/Components/SideMenuItem.test.tsx +30 -0
  207. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +195 -0
  208. package/Tests/UI/Components/XAxis.test.ts +270 -5
  209. package/Tests/UI/Components/XAxisDaylightSaving.test.ts +113 -0
  210. package/Tests/UI/Styles/ChartFocusRing.test.ts +153 -0
  211. package/Tests/UI/Styles/ChartFocusRingCoverage.test.tsx +377 -0
  212. package/Tests/UI/Styles/ThemeStylesheet.ts +162 -0
  213. package/Tests/UI/Utils/TableColumnsToCsv.test.ts +30 -0
  214. package/Tests/UI/Utils/UseDashboardTimeRangeZoom.test.tsx +187 -0
  215. package/Tests/Utils/Dashboard/DashboardResourceListSharedArgs.test.ts +302 -0
  216. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +242 -0
  217. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +507 -0
  218. package/Tests/Utils/Monitor/NetworkDeviceRoleCatalog.test.ts +981 -0
  219. package/Tests/Utils/Monitor/NetworkTopologyRoleCatalog.test.ts +865 -0
  220. package/Tests/Utils/NetworkDevice/DeviceRoleKeyUtil.test.ts +538 -0
  221. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +951 -1
  222. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +238 -0
  223. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +1056 -0
  224. package/Tests/Utils/NetworkDiscovery/ReverseDnsNameUtil.test.ts +882 -0
  225. package/Tests/Utils/Slo/SloEvaluation.test.ts +171 -0
  226. package/Tests/Utils/Slug.test.ts +61 -1
  227. package/Tests/jest.setup.ts +19 -0
  228. package/Types/Date.ts +39 -0
  229. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +61 -0
  230. package/Types/Monitor/SnmpMonitor/SnmpOidListUtil.ts +333 -0
  231. package/Types/NetworkDevice/DefaultNetworkDeviceRole.ts +145 -0
  232. package/Types/Permission.ts +141 -0
  233. package/Types/Rum/SessionReplay.ts +21 -0
  234. package/Types/Telemetry/SourceMap.ts +8 -0
  235. package/UI/Components/Charts/Area/AreaChart.tsx +7 -0
  236. package/UI/Components/Charts/Bar/BarChart.tsx +6 -0
  237. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +9 -1
  238. package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.tsx +176 -0
  239. package/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.ts +745 -0
  240. package/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.tsx +576 -0
  241. package/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.tsx +204 -0
  242. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +554 -545
  243. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +337 -371
  244. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +549 -540
  245. package/UI/Components/Charts/ChartLibrary/Types/FormattedReferenceRegion.ts +7 -3
  246. package/UI/Components/Charts/ChartLibrary/Types/FormattedTimeReferenceLine.ts +14 -3
  247. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +13 -0
  248. package/UI/Components/Charts/Line/LineChart.tsx +7 -0
  249. package/UI/Components/Charts/Types/ChartEventKind.ts +13 -0
  250. package/UI/Components/Charts/Types/ReferenceRegionProps.ts +6 -0
  251. package/UI/Components/Charts/Types/TimeReferenceLineProps.ts +20 -3
  252. package/UI/Components/Charts/Utils/TimeAnnotation.ts +69 -35
  253. package/UI/Components/Charts/Utils/XAxis.ts +125 -18
  254. package/UI/Components/EditionLabel/EditionLabel.tsx +280 -26
  255. package/UI/Components/ErrorMessage/ErrorMessage.tsx +21 -10
  256. package/UI/Components/Feed/FeedItem.tsx +4 -5
  257. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -2
  258. package/UI/Components/Header/UserProfile/UserProfile.tsx +1 -2
  259. package/UI/Components/Image/Image.tsx +20 -3
  260. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  261. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +1 -2
  262. package/UI/Components/ModelTable/BaseModelTable.tsx +200 -39
  263. package/UI/Components/Navbar/NavBar.tsx +62 -13
  264. package/UI/Components/Navbar/NavBarMenuModal.tsx +7 -2
  265. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +2 -0
  266. package/UI/Components/SideMenu/SideMenu.tsx +12 -2
  267. package/UI/Components/SideMenu/SideMenuItem.tsx +4 -1
  268. package/UI/Components/SideMenu/SideMenuSection.tsx +14 -2
  269. package/UI/Components/Table/TableHeader.tsx +64 -48
  270. package/UI/Components/Table/TableRow.tsx +13 -3
  271. package/UI/Styles/Theme.css +38 -0
  272. package/UI/Utils/UseDashboardTimeRangeZoom.ts +77 -0
  273. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +118 -0
  274. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +271 -0
  275. package/Utils/Monitor/NetworkDeviceRoleCatalog.ts +232 -0
  276. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +2 -1
  277. package/Utils/Monitor/NetworkTopologyUtil.ts +116 -33
  278. package/Utils/NetworkDevice/DeviceRoleKeyUtil.ts +137 -0
  279. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +6 -0
  280. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +141 -10
  281. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +55 -3
  282. package/Utils/NetworkDiscovery/ReverseDnsNameUtil.ts +151 -0
  283. package/Utils/Rules/RuleEngineLimits.ts +32 -0
  284. package/Utils/Rules/RulePatternMatchUtil.ts +1 -1
  285. package/Utils/Slug.ts +34 -1
  286. package/build/dist/Models/AnalyticsModels/SecurityEvent.js +48 -16
  287. package/build/dist/Models/AnalyticsModels/SecurityEvent.js.map +1 -1
  288. package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js +10 -5
  289. package/build/dist/Models/AnalyticsModels/ThreatIntelIndicator.js.map +1 -1
  290. package/build/dist/Models/DatabaseModels/AlertSeverity.js +46 -1
  291. package/build/dist/Models/DatabaseModels/AlertSeverity.js.map +1 -1
  292. package/build/dist/Models/DatabaseModels/AlertState.js +0 -2
  293. package/build/dist/Models/DatabaseModels/AlertState.js.map +1 -1
  294. package/build/dist/Models/DatabaseModels/DetectionRule.js +16 -5
  295. package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
  296. package/build/dist/Models/DatabaseModels/Domain.js +1 -1
  297. package/build/dist/Models/DatabaseModels/Domain.js.map +1 -1
  298. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +14 -5
  299. package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +1 -1
  300. package/build/dist/Models/DatabaseModels/Incident.js +1 -1
  301. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  302. package/build/dist/Models/DatabaseModels/IncidentSeverity.js +46 -1
  303. package/build/dist/Models/DatabaseModels/IncidentSeverity.js.map +1 -1
  304. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +1 -1
  305. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  306. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  307. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  308. package/build/dist/Models/DatabaseModels/MetricType.js +0 -2
  309. package/build/dist/Models/DatabaseModels/MetricType.js.map +1 -1
  310. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +0 -2
  311. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  312. package/build/dist/Models/DatabaseModels/MonitorTest.js +0 -2
  313. package/build/dist/Models/DatabaseModels/MonitorTest.js.map +1 -1
  314. package/build/dist/Models/DatabaseModels/NetworkDevice.js +239 -6
  315. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  316. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +88 -0
  317. package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
  318. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  319. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js +521 -0
  320. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js.map +1 -0
  321. package/build/dist/Models/DatabaseModels/NetworkDeviceRole.js +677 -0
  322. package/build/dist/Models/DatabaseModels/NetworkDeviceRole.js.map +1 -0
  323. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -1
  324. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  325. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -1
  326. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  327. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +0 -2
  328. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  329. package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js +0 -2
  330. package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js.map +1 -1
  331. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +0 -2
  332. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
  333. package/build/dist/Models/DatabaseModels/StatusPageResource.js +0 -2
  334. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  335. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +0 -2
  336. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  337. package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js +16 -5
  338. package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js.map +1 -1
  339. package/build/dist/Server/API/GlobalConfigAPI.js +293 -176
  340. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  341. package/build/dist/Server/API/TelemetryAPI.js +31 -3
  342. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  343. package/build/dist/Server/EnvironmentConfig.js +83 -0
  344. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  345. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js +26 -0
  346. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790500000000-AddNetworkDeviceOidTemplate.js.map +1 -0
  347. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js +14 -0
  348. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790600000000-AddAutoImportRuleOidTemplate.js.map +1 -0
  349. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.js +39 -0
  350. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.js.map +1 -0
  351. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  352. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  353. package/build/dist/Server/Middleware/MultipartFormData.js +93 -28
  354. package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
  355. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js +8 -1
  356. package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js.map +1 -1
  357. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js +8 -1
  358. package/build/dist/Server/Services/AlertEpisodeOnCallRuleEngineService.js.map +1 -1
  359. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js +8 -1
  360. package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js.map +1 -1
  361. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js +8 -1
  362. package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js.map +1 -1
  363. package/build/dist/Server/Services/AlertGroupingEngineService.js +8 -1
  364. package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
  365. package/build/dist/Server/Services/AlertLabelRuleEngineService.js +8 -1
  366. package/build/dist/Server/Services/AlertLabelRuleEngineService.js.map +1 -1
  367. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js +8 -1
  368. package/build/dist/Server/Services/AlertOnCallRuleEngineService.js.map +1 -1
  369. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js +8 -1
  370. package/build/dist/Server/Services/AlertOwnerRuleEngineService.js.map +1 -1
  371. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js +8 -1
  372. package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js.map +1 -1
  373. package/build/dist/Server/Services/AlertReminderRuleService.js +8 -1
  374. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  375. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +8 -1
  376. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  377. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +8 -1
  378. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -1
  379. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +8 -1
  380. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
  381. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js +8 -1
  382. package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js.map +1 -1
  383. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +8 -1
  384. package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
  385. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js +8 -1
  386. package/build/dist/Server/Services/DashboardLabelRuleEngineService.js.map +1 -1
  387. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +8 -1
  388. package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
  389. package/build/dist/Server/Services/DatabaseService.js +31 -5
  390. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  391. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js +8 -1
  392. package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js.map +1 -1
  393. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +8 -1
  394. package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
  395. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js +8 -1
  396. package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js.map +1 -1
  397. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +8 -1
  398. package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
  399. package/build/dist/Server/Services/HostLabelRuleEngineService.js +8 -1
  400. package/build/dist/Server/Services/HostLabelRuleEngineService.js.map +1 -1
  401. package/build/dist/Server/Services/HostOwnerRuleEngineService.js +8 -1
  402. package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
  403. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js +8 -1
  404. package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js.map +1 -1
  405. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js +8 -1
  406. package/build/dist/Server/Services/IncidentEpisodeOnCallRuleEngineService.js.map +1 -1
  407. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +8 -1
  408. package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
  409. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js +8 -1
  410. package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js.map +1 -1
  411. package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -1
  412. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  413. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js +8 -1
  414. package/build/dist/Server/Services/IncidentLabelRuleEngineService.js.map +1 -1
  415. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js +8 -1
  416. package/build/dist/Server/Services/IncidentOnCallRuleEngineService.js.map +1 -1
  417. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js +8 -1
  418. package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js.map +1 -1
  419. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js +8 -1
  420. package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js.map +1 -1
  421. package/build/dist/Server/Services/IncidentReminderRuleService.js +8 -1
  422. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  423. package/build/dist/Server/Services/IncidentSlaRuleService.js +8 -1
  424. package/build/dist/Server/Services/IncidentSlaRuleService.js.map +1 -1
  425. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js +8 -1
  426. package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js.map +1 -1
  427. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +8 -1
  428. package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
  429. package/build/dist/Server/Services/Index.js +4 -0
  430. package/build/dist/Server/Services/Index.js.map +1 -1
  431. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js +8 -1
  432. package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js.map +1 -1
  433. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +8 -1
  434. package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
  435. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js +8 -1
  436. package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js.map +1 -1
  437. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +8 -1
  438. package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
  439. package/build/dist/Server/Services/KubernetesResourceService.js +5 -1
  440. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  441. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js +8 -1
  442. package/build/dist/Server/Services/MonitorLabelRuleEngineService.js.map +1 -1
  443. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js +8 -1
  444. package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js.map +1 -1
  445. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +30 -21
  446. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  447. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +64 -0
  448. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  449. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +8 -1
  450. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
  451. package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js +180 -0
  452. package/build/dist/Server/Services/NetworkDeviceOidTemplateService.js.map +1 -0
  453. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +8 -1
  454. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
  455. package/build/dist/Server/Services/NetworkDeviceRoleService.js +70 -0
  456. package/build/dist/Server/Services/NetworkDeviceRoleService.js.map +1 -0
  457. package/build/dist/Server/Services/NetworkDeviceService.js +154 -1
  458. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  459. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js +8 -1
  460. package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js.map +1 -1
  461. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +8 -1
  462. package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
  463. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js +8 -1
  464. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js.map +1 -1
  465. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +8 -1
  466. package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
  467. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js +8 -1
  468. package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js.map +1 -1
  469. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +8 -1
  470. package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
  471. package/build/dist/Server/Services/ProjectService.js +65 -0
  472. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  473. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +8 -1
  474. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -1
  475. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +8 -1
  476. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
  477. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js +8 -1
  478. package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js.map +1 -1
  479. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +8 -1
  480. package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
  481. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js +8 -1
  482. package/build/dist/Server/Services/RunbookLabelRuleEngineService.js.map +1 -1
  483. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +8 -1
  484. package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
  485. package/build/dist/Server/Services/RunbookRuleEngineService.js +8 -1
  486. package/build/dist/Server/Services/RunbookRuleEngineService.js.map +1 -1
  487. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js +8 -1
  488. package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js.map +1 -1
  489. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js +8 -1
  490. package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js.map +1 -1
  491. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +8 -1
  492. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  493. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js +8 -1
  494. package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js.map +1 -1
  495. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +8 -1
  496. package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
  497. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js +8 -1
  498. package/build/dist/Server/Services/ServiceLabelRuleEngineService.js.map +1 -1
  499. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +8 -1
  500. package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
  501. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js +8 -1
  502. package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js.map +1 -1
  503. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +6 -0
  504. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -1
  505. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js +8 -1
  506. package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js.map +1 -1
  507. package/build/dist/Server/Services/TelemetrySourceMapService.js +213 -19
  508. package/build/dist/Server/Services/TelemetrySourceMapService.js.map +1 -1
  509. package/build/dist/Server/Services/UserNotificationSettingService.js +0 -1
  510. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  511. package/build/dist/Server/Services/UserService.js +25 -0
  512. package/build/dist/Server/Services/UserService.js.map +1 -1
  513. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js +8 -1
  514. package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js.map +1 -1
  515. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +8 -1
  516. package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
  517. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +145 -0
  518. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +1 -0
  519. package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js +201 -0
  520. package/build/dist/Server/Utils/Monitor/CapturedMetricAttributeUtil.js.map +1 -0
  521. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +19 -2
  522. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  523. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +42 -23
  524. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  525. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +29 -0
  526. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  527. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +11 -2
  528. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
  529. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +16 -1
  530. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
  531. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +10 -0
  532. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  533. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +33 -0
  534. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  535. package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js +37 -0
  536. package/build/dist/Server/Utils/Rules/RuleEngineRuleRead.js.map +1 -0
  537. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +12 -1
  538. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -1
  539. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +14 -2
  540. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  541. package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js +15 -5
  542. package/build/dist/Server/Utils/Telemetry/SourceMapResolver.js.map +1 -1
  543. package/build/dist/Server/Utils/Telemetry/StackTraceParser.js +307 -0
  544. package/build/dist/Server/Utils/Telemetry/StackTraceParser.js.map +1 -1
  545. package/build/dist/Types/Date.js +27 -0
  546. package/build/dist/Types/Date.js.map +1 -1
  547. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js +253 -0
  548. package/build/dist/Types/Monitor/SnmpMonitor/SnmpOidListUtil.js.map +1 -0
  549. package/build/dist/Types/NetworkDevice/DefaultNetworkDeviceRole.js +105 -0
  550. package/build/dist/Types/NetworkDevice/DefaultNetworkDeviceRole.js.map +1 -0
  551. package/build/dist/Types/Permission.js +126 -0
  552. package/build/dist/Types/Permission.js.map +1 -1
  553. package/build/dist/Types/Rum/SessionReplay.js +19 -0
  554. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  555. package/build/dist/UI/Components/Charts/Area/AreaChart.js +1 -1
  556. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  557. package/build/dist/UI/Components/Charts/Bar/BarChart.js +1 -1
  558. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  559. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -1
  560. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  561. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js +83 -0
  562. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationHoverCard.js.map +1 -0
  563. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js +531 -0
  564. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/AnnotationLayout.js.map +1 -0
  565. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js +264 -0
  566. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/ChartAnnotationLayer.js.map +1 -0
  567. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js +122 -0
  568. package/build/dist/UI/Components/Charts/ChartLibrary/Annotations/UseChartAnnotations.js.map +1 -0
  569. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +262 -234
  570. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  571. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +146 -153
  572. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  573. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +261 -233
  574. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  575. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +14 -0
  576. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -0
  577. package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
  578. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  579. package/build/dist/UI/Components/Charts/Types/ChartEventKind.js +14 -0
  580. package/build/dist/UI/Components/Charts/Types/ChartEventKind.js.map +1 -0
  581. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js +57 -28
  582. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js.map +1 -1
  583. package/build/dist/UI/Components/Charts/Utils/XAxis.js +103 -17
  584. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  585. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +198 -23
  586. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  587. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +15 -5
  588. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
  589. package/build/dist/UI/Components/Feed/FeedItem.js +4 -5
  590. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  591. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -2
  592. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  593. package/build/dist/UI/Components/Header/UserProfile/UserProfile.js.map +1 -1
  594. package/build/dist/UI/Components/Image/Image.js +6 -1
  595. package/build/dist/UI/Components/Image/Image.js.map +1 -1
  596. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  597. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  598. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +1 -2
  599. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  600. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +143 -36
  601. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  602. package/build/dist/UI/Components/Navbar/NavBar.js +44 -9
  603. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  604. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
  605. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  606. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +1 -1
  607. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  608. package/build/dist/UI/Components/SideMenu/SideMenu.js +9 -6
  609. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  610. package/build/dist/UI/Components/SideMenu/SideMenuItem.js +1 -1
  611. package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
  612. package/build/dist/UI/Components/SideMenu/SideMenuSection.js +7 -2
  613. package/build/dist/UI/Components/SideMenu/SideMenuSection.js.map +1 -1
  614. package/build/dist/UI/Components/Table/TableHeader.js +20 -22
  615. package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
  616. package/build/dist/UI/Components/Table/TableRow.js +13 -4
  617. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  618. package/build/dist/UI/Utils/UseDashboardTimeRangeZoom.js +42 -0
  619. package/build/dist/UI/Utils/UseDashboardTimeRangeZoom.js.map +1 -0
  620. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +74 -0
  621. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -0
  622. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +153 -0
  623. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -0
  624. package/build/dist/Utils/Monitor/NetworkDeviceRoleCatalog.js +126 -0
  625. package/build/dist/Utils/Monitor/NetworkDeviceRoleCatalog.js.map +1 -0
  626. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +2 -1
  627. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
  628. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +67 -32
  629. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  630. package/build/dist/Utils/NetworkDevice/DeviceRoleKeyUtil.js +114 -0
  631. package/build/dist/Utils/NetworkDevice/DeviceRoleKeyUtil.js.map +1 -0
  632. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  633. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +127 -7
  634. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
  635. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +50 -3
  636. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  637. package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js +132 -0
  638. package/build/dist/Utils/NetworkDiscovery/ReverseDnsNameUtil.js.map +1 -0
  639. package/build/dist/Utils/Rules/RuleEngineLimits.js +31 -0
  640. package/build/dist/Utils/Rules/RuleEngineLimits.js.map +1 -0
  641. package/build/dist/Utils/Rules/RulePatternMatchUtil.js +1 -1
  642. package/build/dist/Utils/Slug.js +31 -1
  643. package/build/dist/Utils/Slug.js.map +1 -1
  644. package/jest.config.json +1 -0
  645. package/package.json +2 -2
@@ -0,0 +1,882 @@
1
+ import {
2
+ MAX_REVERSE_DNS_LABEL_LENGTH,
3
+ MAX_REVERSE_DNS_NAME_LENGTH,
4
+ normalizeReverseDnsName,
5
+ } from "../../../Utils/NetworkDiscovery/ReverseDnsNameUtil";
6
+ import Slug from "../../../Utils/Slug";
7
+ import { describe, expect, it } from "@jest/globals";
8
+
9
+ /*
10
+ * OneUptime issue #3529 — discovery results should carry the reverse-DNS name
11
+ * of each host, not just its address.
12
+ *
13
+ * This is the gate on the ONE field in a scan result whose value is chosen by
14
+ * the scanned network rather than by OneUptime or by the operator. A PTR
15
+ * record is published by whoever runs DNS for the subnet being swept, and a
16
+ * discovery scan is routinely pointed at a subnet this project does not
17
+ * administer — so what comes back is untrusted input that goes on to become a
18
+ * rendered line in the Review dialog, a NetworkDevice's name, and that
19
+ * device's slug.
20
+ *
21
+ * The suite therefore has two halves that pull in opposite directions, and
22
+ * both matter:
23
+ *
24
+ * - Real names must survive. A rule that is too strict silently drops the
25
+ * very names the issue asked for, and does it invisibly: the operator
26
+ * sees the address they always saw and has no way to tell "no PTR record"
27
+ * from "we threw your PTR record away". Underscores, single labels,
28
+ * dashes-with-digits, a purely numeric label sitting next to alphabetic
29
+ * ones, and long FQDNs are all real, and all pass.
30
+ * - Everything that is not a hostname must not. Whitespace, quotes, angle
31
+ * brackets, control characters, non-ASCII and over-length values are what
32
+ * a hostile or broken reverse zone answers with, and they must never
33
+ * reach a name column.
34
+ *
35
+ * The two rules that are written as a quantifier over labels — "reject when
36
+ * EVERY label is numeric" and "reject when the name ends AT A LABEL BOUNDARY
37
+ * with a reverse-lookup zone" — are the ones where a one-token edit (`every`
38
+ * to `some`, `.${zone}` to `zone`) turns a narrow rejection into a broad one
39
+ * and deletes real names without a single error surfacing anywhere. Those two
40
+ * survive-directions have describe blocks of their own below.
41
+ *
42
+ * Every non-printing and non-ASCII character in this file is written as a
43
+ * \uXXXX escape rather than pasted literally. The whole point of several of
44
+ * these cases is that the character is INVISIBLE — a literal NUL, zero-width
45
+ * joiner or right-to-left override in the source is unreviewable in a diff,
46
+ * survives a careless copy-paste into the source it is meant to guard, and is
47
+ * mangled by tooling that normalises text. The escape is the test.
48
+ */
49
+
50
+ describe("normalizeReverseDnsName — names that must survive", () => {
51
+ it("accepts an ordinary FQDN", () => {
52
+ expect(normalizeReverseDnsName("core-switch-01.corp.example.com")).toBe(
53
+ "core-switch-01.corp.example.com",
54
+ );
55
+ });
56
+
57
+ it("accepts a single-label name", () => {
58
+ /*
59
+ * Flat internal zones and small networks publish bare names, and
60
+ * "printer-3" is a strictly better answer for the Review dialog than
61
+ * 10.18.166.51. Rejecting single labels would throw those away.
62
+ */
63
+ expect(normalizeReverseDnsName("printer-3")).toBe("printer-3");
64
+ });
65
+
66
+ it("accepts underscores, which are illegal in a hostname but common in the wild", () => {
67
+ /*
68
+ * Windows/DHCP-registered names and hand-authored reverse zones use them
69
+ * constantly. The rule here is "is this safe to store and render", not
70
+ * "does this satisfy RFC 952".
71
+ */
72
+ expect(normalizeReverseDnsName("ws_finance_12.corp.example.com")).toBe(
73
+ "ws_finance_12.corp.example.com",
74
+ );
75
+ });
76
+
77
+ it("accepts a generated name built out of the address's octets", () => {
78
+ /*
79
+ * NOT all-numeric — the octets are joined by dashes into one label — so
80
+ * this is a real name that happens to encode the address, and it carries
81
+ * the zone it lives in. It is more useful than the bare address.
82
+ */
83
+ expect(normalizeReverseDnsName("10-18-166-51.dhcp.corp.example.com")).toBe(
84
+ "10-18-166-51.dhcp.corp.example.com",
85
+ );
86
+ });
87
+
88
+ it("strips exactly one trailing root dot", () => {
89
+ expect(normalizeReverseDnsName("host.example.com.")).toBe(
90
+ "host.example.com",
91
+ );
92
+ });
93
+
94
+ it("trims surrounding whitespace", () => {
95
+ expect(normalizeReverseDnsName(" host.example.com ")).toBe(
96
+ "host.example.com",
97
+ );
98
+ });
99
+
100
+ it("preserves case as the reverse zone authored it", () => {
101
+ /*
102
+ * DNS is case-insensitive, which is a reason not to COMPARE on case — not
103
+ * a reason to discard it. "SW-Core-01" is how somebody wrote it down, and
104
+ * that is as close to operator intent as this data gets.
105
+ */
106
+ expect(normalizeReverseDnsName("SW-Core-01.Corp.Example.COM")).toBe(
107
+ "SW-Core-01.Corp.Example.COM",
108
+ );
109
+ });
110
+ });
111
+
112
+ describe("normalizeReverseDnsName — a numeric label beside alphabetic ones", () => {
113
+ /*
114
+ * The single most load-bearing acceptance in the file, and the one the rest
115
+ * of the suite could not see.
116
+ *
117
+ * The address-restating rule is "reject when EVERY label is numeric". Its
118
+ * rejection direction is pinned three times over ("10.18.166.51", "51",
119
+ * "10.18"). Its SURVIVE direction — a name that merely CONTAINS an
120
+ * all-digit label — had no case at all, so rewriting
121
+ * `labels.every(isNumeric)` as `labels.some(isNumeric)` left the whole
122
+ * suite green while deleting exactly the names issue #3529 is about: a
123
+ * reverse zone that publishes each host as `<last octet>.<zone>`, so hosts
124
+ * .51, .53, .54 and .55 answer "51.corp.example.com" and friends. Under the
125
+ * `some` mutation every one of those hosts silently loses its name and the
126
+ * operator sees the addresses they always saw, with nothing logged.
127
+ *
128
+ * These cases fail under that mutation and pass under the real rule, which
129
+ * is the whole reason they are here.
130
+ */
131
+
132
+ it("accepts a name whose leading label is purely numeric", () => {
133
+ /*
134
+ * The canonical shape from the issue: the PTR for 10.18.166.51 in a zone
135
+ * that names hosts by octet. It is not the address restated — "corp" and
136
+ * "example" and "com" are not digits — so it is a real name and must be
137
+ * kept.
138
+ */
139
+ expect(normalizeReverseDnsName("51.corp.example.com")).toBe(
140
+ "51.corp.example.com",
141
+ );
142
+ expect(normalizeReverseDnsName("10.corp.example.com")).toBe(
143
+ "10.corp.example.com",
144
+ );
145
+
146
+ /*
147
+ * "0.pool.ntp.org" is the real-world one: a leading zero, a single digit,
148
+ * and a name millions of hosts genuinely resolve to. If a numeric label
149
+ * anywhere were disqualifying, this would come back undefined.
150
+ */
151
+ expect(normalizeReverseDnsName("0.pool.ntp.org")).toBe("0.pool.ntp.org");
152
+ });
153
+
154
+ it("accepts a numeric label in an interior or trailing position", () => {
155
+ /*
156
+ * Position must not matter either — the rule is a quantifier over ALL
157
+ * labels, not an inspection of the first one. A rule written as "reject
158
+ * if the first label is numeric" would pass the case above's mutation
159
+ * test but fail here, so both shapes are pinned.
160
+ */
161
+ expect(normalizeReverseDnsName("gw.51.example.com")).toBe(
162
+ "gw.51.example.com",
163
+ );
164
+ expect(normalizeReverseDnsName("printer.3")).toBe("printer.3");
165
+ expect(normalizeReverseDnsName("a.1")).toBe("a.1");
166
+ });
167
+ });
168
+
169
+ describe("normalizeReverseDnsName — length boundaries", () => {
170
+ /*
171
+ * These are the off-by-one seams in the function, pinned on both sides.
172
+ * Each one is a place where a "<" that should be "<=" either silently
173
+ * discards a legitimate long FQDN (the failure nobody notices, because the
174
+ * dialog just shows the address it always showed) or lets an over-length
175
+ * value through into a varchar column.
176
+ */
177
+
178
+ const LABEL_AT_LIMIT: string = "a".repeat(MAX_REVERSE_DNS_LABEL_LENGTH);
179
+
180
+ /*
181
+ * 3 * 63 + 3 separators + 61 = 253. Built out of maximum-length labels so
182
+ * the per-label rule cannot be what decides any of the whole-name cases
183
+ * below — only the whole-name limit can. Note that this arithmetic is
184
+ * load-bearing on MAX_REVERSE_DNS_LABEL_LENGTH staying 63: the
185
+ * `toHaveLength` assertions below are what re-derive both constants, so a
186
+ * change to either number turns this block red rather than silently
187
+ * shifting what "at the limit" means.
188
+ */
189
+ const NAME_AT_LIMIT: string = `${LABEL_AT_LIMIT}.${LABEL_AT_LIMIT}.${LABEL_AT_LIMIT}.${"b".repeat(61)}`;
190
+
191
+ it("accepts a label of exactly the maximum length and rejects one character more", () => {
192
+ expect(LABEL_AT_LIMIT).toHaveLength(63);
193
+ expect(normalizeReverseDnsName(`${LABEL_AT_LIMIT}.example.com`)).toBe(
194
+ `${LABEL_AT_LIMIT}.example.com`,
195
+ );
196
+
197
+ const overLimit: string = "a".repeat(MAX_REVERSE_DNS_LABEL_LENGTH + 1);
198
+ expect(normalizeReverseDnsName(`${overLimit}.example.com`)).toBeUndefined();
199
+ });
200
+
201
+ it("accepts a name that is one label of exactly the maximum length", () => {
202
+ /*
203
+ * The label limit and the name limit are different numbers, and a single
204
+ * maximal label exercises both at once: it must be judged by the label
205
+ * rule (63) and pass the name rule (253) without either being applied to
206
+ * the other's value.
207
+ */
208
+ expect(normalizeReverseDnsName(LABEL_AT_LIMIT)).toBe(LABEL_AT_LIMIT);
209
+ });
210
+
211
+ it("accepts a name of exactly the maximum length and rejects one character more", () => {
212
+ expect(NAME_AT_LIMIT).toHaveLength(MAX_REVERSE_DNS_NAME_LENGTH);
213
+ expect(normalizeReverseDnsName(NAME_AT_LIMIT)).toBe(NAME_AT_LIMIT);
214
+
215
+ const overLimit: string = `${LABEL_AT_LIMIT}.${LABEL_AT_LIMIT}.${LABEL_AT_LIMIT}.${"b".repeat(62)}`;
216
+ expect(overLimit).toHaveLength(MAX_REVERSE_DNS_NAME_LENGTH + 1);
217
+ expect(normalizeReverseDnsName(overLimit)).toBeUndefined();
218
+ });
219
+
220
+ it("accepts a name whose trailing dot is what pushes it over the limit", () => {
221
+ /*
222
+ * The root label is not part of the name, so a 253-character name written
223
+ * fully qualified is 254 characters of INPUT and must still be accepted.
224
+ * Measuring before stripping would reject it — and the resolvers that
225
+ * emit fully qualified answers are exactly the well-run ones whose names
226
+ * are worth having.
227
+ */
228
+ expect(`${NAME_AT_LIMIT}.`).toHaveLength(MAX_REVERSE_DNS_NAME_LENGTH + 1);
229
+ expect(normalizeReverseDnsName(`${NAME_AT_LIMIT}.`)).toBe(NAME_AT_LIMIT);
230
+ });
231
+
232
+ it("trims before it strips the root label and before it measures", () => {
233
+ /*
234
+ * The two orderings the cases above leave open, pinned in one input.
235
+ * " <253 chars>. " is 258 characters of raw input, and it is the answer
236
+ * a well-run resolver's fully qualified reply looks like once it has been
237
+ * through a text field or a log line that padded it.
238
+ *
239
+ * Reasoned against the mutated implementations:
240
+ * - measure-then-trim: 258 > 253, rejected, name silently lost.
241
+ * - strip-root-then-trim: the raw value ends in a SPACE, so nothing is
242
+ * stripped; after trimming, the trailing dot survives into the label
243
+ * split as an empty final label, and the name is rejected.
244
+ * Only trim -> strip -> measure returns NAME_AT_LIMIT, so this case dies
245
+ * under either reordering.
246
+ *
247
+ * It also pins the returned value byte-for-byte at the maximum length:
248
+ * what comes back is the trimmed, root-stripped INPUT, unmodified and
249
+ * untruncated.
250
+ */
251
+ const padded: string = ` ${NAME_AT_LIMIT}. `;
252
+ expect(padded).toHaveLength(MAX_REVERSE_DNS_NAME_LENGTH + 5);
253
+ expect(normalizeReverseDnsName(padded)).toBe(NAME_AT_LIMIT);
254
+ });
255
+
256
+ it("rejects an absurdly long value made entirely of legal labels", () => {
257
+ /*
258
+ * Ten thousand characters in which EVERY label is a legal two-character
259
+ * label, so the per-label rule has nothing to object to and the whole-name
260
+ * limit is the only thing that can reject it. Delete the
261
+ * MAX_REVERSE_DNS_NAME_LENGTH check and this value is accepted — a
262
+ * ten-thousand-character "hostname" on its way to a varchar(100) column.
263
+ *
264
+ * (Its earlier form, "a".repeat(10000), was a single 10,000-character
265
+ * label and so was rejected by the 63-character label rule first; it
266
+ * could not fail if the name limit were removed, which is the point of
267
+ * building this one out of legal labels instead.)
268
+ */
269
+ const legalLabelsOverLimit: string = "ab.".repeat(3334);
270
+ expect(legalLabelsOverLimit.length).toBeGreaterThan(
271
+ MAX_REVERSE_DNS_NAME_LENGTH,
272
+ );
273
+ expect(normalizeReverseDnsName(legalLabelsOverLimit)).toBeUndefined();
274
+ });
275
+ });
276
+
277
+ describe("normalizeReverseDnsName — answers that restate the address", () => {
278
+ /*
279
+ * The entire premise of the feature is that a name tells the operator more
280
+ * than an address does. A "name" that IS the address is not a name — and
281
+ * worse, presenting it as one asserts a resolved hostname that nobody
282
+ * published. These must fall through so the caller uses the address it
283
+ * already had.
284
+ *
285
+ * The complement — a numeric label that sits BESIDE alphabetic ones and
286
+ * must survive — is its own describe block above; neither half of the rule
287
+ * is safe without the other.
288
+ */
289
+
290
+ it("rejects a dotted-quad", () => {
291
+ expect(normalizeReverseDnsName("10.18.166.51")).toBeUndefined();
292
+ });
293
+
294
+ it("rejects a single numeric label", () => {
295
+ expect(normalizeReverseDnsName("51")).toBeUndefined();
296
+ });
297
+
298
+ it("rejects a partially numeric answer with no alphabetic label", () => {
299
+ expect(normalizeReverseDnsName("10.18")).toBeUndefined();
300
+ });
301
+
302
+ it("rejects an in-addr.arpa query name echoed back", () => {
303
+ /*
304
+ * Some resolvers hand the query name back on certain failures. It passes
305
+ * every character rule and is a strictly worse label than the address it
306
+ * was derived from.
307
+ */
308
+ expect(
309
+ normalizeReverseDnsName("51.166.18.10.in-addr.arpa"),
310
+ ).toBeUndefined();
311
+ expect(
312
+ normalizeReverseDnsName("51.166.18.10.IN-ADDR.ARPA."),
313
+ ).toBeUndefined();
314
+ });
315
+
316
+ it("rejects an ip6.arpa query name echoed back", () => {
317
+ expect(
318
+ normalizeReverseDnsName(
319
+ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa",
320
+ ),
321
+ ).toBeUndefined();
322
+ });
323
+ });
324
+
325
+ describe("normalizeReverseDnsName — the reverse zones as an apex", () => {
326
+ /*
327
+ * The suffix rule alone left a hole: a resolver that echoes a TRUNCATED
328
+ * query name, or a zone whose PTR points at the zone apex itself, answers
329
+ * with the bare zone. "in-addr.arpa" is a perfectly well-formed hostname
330
+ * string — right charset, right label lengths, not all-numeric — so
331
+ * everything else in this function waves it through, and the operator gets
332
+ * a fleet of devices all called "in-addr.arpa" instead of their addresses.
333
+ * That is why the check is `lowerCased === zone` as well as `endsWith`.
334
+ */
335
+
336
+ it("rejects the bare in-addr.arpa apex", () => {
337
+ expect(normalizeReverseDnsName("in-addr.arpa")).toBeUndefined();
338
+ });
339
+
340
+ it("rejects the bare ip6.arpa apex", () => {
341
+ expect(normalizeReverseDnsName("ip6.arpa")).toBeUndefined();
342
+ });
343
+
344
+ it("rejects the apex with a trailing root dot, in any case", () => {
345
+ /*
346
+ * Two orderings in one case, because the separate lower-case-with-dot
347
+ * test was fully contained in this one's inputs and pinned nothing extra.
348
+ *
349
+ * Root-dot ordering: the apex comparison runs AFTER the root label is
350
+ * stripped. Reversed, "in-addr.arpa." would match neither the apex (extra
351
+ * dot) nor the suffix (nothing before it) and would sail through.
352
+ *
353
+ * Case ordering: DNS is case-insensitive and resolvers echo whatever case
354
+ * they were handed — 0x20 query randomisation makes mixed case the NORMAL
355
+ * shape of an echoed name, not an exotic one. Comparing the raw string
356
+ * would accept "In-Addr.ARPA"; lower-casing the value but not comparing
357
+ * against a lower-case zone would reject everything.
358
+ */
359
+ expect(normalizeReverseDnsName("in-addr.arpa.")).toBeUndefined();
360
+ expect(normalizeReverseDnsName("ip6.arpa.")).toBeUndefined();
361
+ expect(normalizeReverseDnsName("IN-ADDR.ARPA")).toBeUndefined();
362
+ expect(normalizeReverseDnsName("In-Addr.Arpa")).toBeUndefined();
363
+ expect(normalizeReverseDnsName("iN-aDdR.aRpA.")).toBeUndefined();
364
+ expect(normalizeReverseDnsName("IP6.ARPA")).toBeUndefined();
365
+ expect(normalizeReverseDnsName("Ip6.Arpa.")).toBeUndefined();
366
+ });
367
+
368
+ it("keeps a real name that merely CONTAINS arpa", () => {
369
+ // The suffix rule must be a suffix rule, not a substring rule.
370
+ expect(normalizeReverseDnsName("arpa-gw.example.com")).toBe(
371
+ "arpa-gw.example.com",
372
+ );
373
+ expect(normalizeReverseDnsName("in-addr.arpa.example.com")).toBe(
374
+ "in-addr.arpa.example.com",
375
+ );
376
+ });
377
+
378
+ it("matches the zone at a label boundary, not as a bare string suffix", () => {
379
+ /*
380
+ * The other half of "it is a suffix rule", and the one the CONTAINS cases
381
+ * above could not see. Every accepted arpa name in this file
382
+ * ("arpa-gw.example.com", "in-addr.arpa.example.com",
383
+ * "gw.arpa.example.com", "something.arpa") ends somewhere OTHER than the
384
+ * zone string, so rewriting `lowerCased.endsWith(`.${zone}`)` as
385
+ * `lowerCased.endsWith(zone)` — dropping one character — survived the
386
+ * entire suite.
387
+ *
388
+ * Under that mutation "notin-addr.arpa" and "voip6.arpa" are rejected:
389
+ * both end with the zone's characters while their final labels are
390
+ * "notin-addr" and "voip6", which are somebody's real hostnames and are
391
+ * nothing to do with a reverse-lookup zone. The dot is what makes the
392
+ * difference between "this name lives IN the zone" and "this name happens
393
+ * to end with those letters", so it is asserted here.
394
+ */
395
+ expect(normalizeReverseDnsName("notin-addr.arpa")).toBe("notin-addr.arpa");
396
+ expect(normalizeReverseDnsName("voip6.arpa")).toBe("voip6.arpa");
397
+ expect(normalizeReverseDnsName("myip6.arpa")).toBe("myip6.arpa");
398
+
399
+ /*
400
+ * ...while the genuine label-boundary forms of the same strings stay
401
+ * rejected, so the case above cannot be satisfied by deleting the zone
402
+ * rule outright.
403
+ */
404
+ expect(normalizeReverseDnsName("x.in-addr.arpa")).toBeUndefined();
405
+ expect(normalizeReverseDnsName("x.ip6.arpa")).toBeUndefined();
406
+ });
407
+
408
+ it("keeps a name with an interior or trailing label called arpa", () => {
409
+ /*
410
+ * ACCEPTED, deliberately, and this is the line the apex check must not
411
+ * cross. The rule targets the two REVERSE-LOOKUP zones, not the .arpa
412
+ * TLD: `arpa` is a real, delegated top-level domain, and "gw.arpa" or
413
+ * "gw.arpa.example.com" is a name somebody published on purpose. It is
414
+ * still a better label for the Review dialog than 10.18.166.51, which is
415
+ * the only thing the operator would otherwise see.
416
+ *
417
+ * Widening the check to "ends in .arpa" would silently delete these — the
418
+ * exact invisible over-rejection this suite exists to prevent — and would
419
+ * buy nothing, because the harm is specific to the two zones whose names
420
+ * merely restate the address.
421
+ */
422
+ expect(normalizeReverseDnsName("gw.arpa.example.com")).toBe(
423
+ "gw.arpa.example.com",
424
+ );
425
+ expect(normalizeReverseDnsName("something.arpa")).toBe("something.arpa");
426
+ });
427
+ });
428
+
429
+ describe("normalizeReverseDnsName — values that are not names at all", () => {
430
+ it("rejects empty and whitespace-only input", () => {
431
+ expect(normalizeReverseDnsName("")).toBeUndefined();
432
+ expect(normalizeReverseDnsName(" ")).toBeUndefined();
433
+ expect(normalizeReverseDnsName("\t\n")).toBeUndefined();
434
+ });
435
+
436
+ it("rejects a bare root label", () => {
437
+ expect(normalizeReverseDnsName(".")).toBeUndefined();
438
+ });
439
+
440
+ it("rejects empty labels", () => {
441
+ /*
442
+ * Only ONE trailing dot is the root label. A second leaves an empty final
443
+ * label, which is malformed rather than a typo to repair — inventing the
444
+ * repair would be inventing a name.
445
+ */
446
+ expect(normalizeReverseDnsName("host.example.com..")).toBeUndefined();
447
+ expect(normalizeReverseDnsName("host..example.com")).toBeUndefined();
448
+ expect(normalizeReverseDnsName(".host.example.com")).toBeUndefined();
449
+ });
450
+
451
+ it("rejects a leading or trailing hyphen in a label", () => {
452
+ expect(normalizeReverseDnsName("-host.example.com")).toBeUndefined();
453
+ expect(normalizeReverseDnsName("host-.example.com")).toBeUndefined();
454
+ });
455
+
456
+ it("rejects whitespace inside the name", () => {
457
+ /*
458
+ * " " is truthy, and a name with a space in it renders as a plausible
459
+ * device name and slugifies into something else entirely. This is the
460
+ * same class of bug normalizeDiscoveredHosts fixed for ipAddress.
461
+ */
462
+ expect(normalizeReverseDnsName("core switch.example.com")).toBeUndefined();
463
+ expect(normalizeReverseDnsName("host.example .com")).toBeUndefined();
464
+ });
465
+
466
+ it("rejects markup, quotes and path characters", () => {
467
+ /*
468
+ * React escapes on render, so this is not an XSS test — it is a "this was
469
+ * never a hostname" test. A reverse zone that answers with any of these
470
+ * is broken or hostile, and either way the address is the better answer.
471
+ */
472
+ const hostile: Array<string> = [
473
+ "<script>alert(1)</script>",
474
+ '"><img src=x onerror=alert(1)>',
475
+ "host.example.com/../../etc/passwd",
476
+ "host.example.com:8080",
477
+ "host@example.com",
478
+ "host;rm -rf /",
479
+ "host,example,com",
480
+ "host\\example",
481
+ "host%00.example.com",
482
+ "host|example.com",
483
+ "$(whoami).example.com",
484
+ "{{constructor}}.example.com",
485
+ ];
486
+
487
+ for (const value of hostile) {
488
+ expect(normalizeReverseDnsName(value)).toBeUndefined();
489
+ }
490
+ });
491
+
492
+ it("rejects control characters, including ones that survive a trim", () => {
493
+ /*
494
+ * trim() only removes whitespace at the ENDS, so an interior control
495
+ * character reaches the charset check or nothing does. A newline puts a
496
+ * second line into a name column and into the Review dialog row; a NUL
497
+ * truncates the value in anything that reaches a C string boundary
498
+ * (Postgres rejects it outright in a text column, failing the import);
499
+ * ESC starts a terminal escape sequence in probe and server logs, which
500
+ * is where an operator reads these names back.
501
+ *
502
+ * Written as escapes on purpose — see the file header.
503
+ */
504
+ expect(normalizeReverseDnsName("host\n.example.com")).toBeUndefined();
505
+ expect(normalizeReverseDnsName("host\r\n.example.com")).toBeUndefined();
506
+ // U+0000 NUL.
507
+ expect(normalizeReverseDnsName("host.exa\u0000mple.com")).toBeUndefined();
508
+ // U+001B ESC — the lead byte of an ANSI terminal escape sequence.
509
+ expect(normalizeReverseDnsName("host.exa\u001Bmple.com")).toBeUndefined();
510
+ // U+007F DEL.
511
+ expect(normalizeReverseDnsName("host\u007F.example.com")).toBeUndefined();
512
+ });
513
+
514
+ it("rejects non-ASCII, including homoglyphs of a legitimate name", () => {
515
+ /*
516
+ * Punycode ("xn--...") is the encoding a real internationalised name
517
+ * arrives in and is pure ASCII, so rejecting non-ASCII rejects nothing a
518
+ * resolver would actually answer with.
519
+ */
520
+ // U+0441 CYRILLIC SMALL LETTER ES — renders identically to Latin "c".
521
+ expect(
522
+ normalizeReverseDnsName("\u0441ore-switch.example.com"),
523
+ ).toBeUndefined();
524
+ // U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX.
525
+ expect(normalizeReverseDnsName("h\u00F4te.example.com")).toBeUndefined();
526
+ // U+200B ZERO WIDTH SPACE — invisible, and not stripped by trim().
527
+ expect(normalizeReverseDnsName("router\u200B.example.com")).toBeUndefined();
528
+ // ...while the punycode form of the same idea is fine.
529
+ expect(normalizeReverseDnsName("xn--hte-snab.example.com")).toBe(
530
+ "xn--hte-snab.example.com",
531
+ );
532
+ });
533
+
534
+ it("rejects unicode that renders as ASCII to a human reading the dialog", () => {
535
+ /*
536
+ * This is the class the charset rule exists for. Every one of these
537
+ * renders in the Review dialog as something the operator would read as an
538
+ * ordinary hostname, while being a different string underneath — so an
539
+ * operator ticking "core-switch" cannot be shown a device that is not it.
540
+ * A blocklist of "suspicious" codepoints would be an arms race; the
541
+ * ASCII-only rule ends it, and costs nothing because PTR answers are
542
+ * ASCII on the wire.
543
+ */
544
+ // U+FF48 U+FF4F U+FF53 U+FF54 — FULLWIDTH h, o, s, t.
545
+ expect(
546
+ normalizeReverseDnsName("\uFF48\uFF4F\uFF53\uFF54.example.com"),
547
+ ).toBeUndefined();
548
+ // U+0430 CYRILLIC SMALL LETTER A inside an otherwise Latin word.
549
+ expect(normalizeReverseDnsName("g\u0430teway.example.com")).toBeUndefined();
550
+ // U+200D ZERO WIDTH JOINER — invisible, and splits a word for search.
551
+ expect(
552
+ normalizeReverseDnsName("core\u200Dswitch.example.com"),
553
+ ).toBeUndefined();
554
+ // U+202E RIGHT-TO-LEFT OVERRIDE — reverses everything rendered after it.
555
+ expect(
556
+ normalizeReverseDnsName("host\u202Emoc.elpmaxe.example.com"),
557
+ ).toBeUndefined();
558
+ /*
559
+ * U+FEFF ZERO WIDTH NO-BREAK SPACE (BOM) in the middle, where trim() is
560
+ * no help.
561
+ */
562
+ expect(
563
+ normalizeReverseDnsName("host\uFEFFname.example.com"),
564
+ ).toBeUndefined();
565
+ });
566
+ });
567
+
568
+ describe("normalizeReverseDnsName — hostile-looking values that the charset allows", () => {
569
+ /*
570
+ * These pass every rule in the function, and that is the correct outcome:
571
+ * this gate decides "is this string shaped like a hostname", not "is this
572
+ * string a good idea". Pinning the acceptances matters as much as pinning
573
+ * the rejections, because each one is a place somebody might later be
574
+ * tempted to add a blocklist — and a blocklist here would silently delete
575
+ * real names while the actual defence lives where the value is USED.
576
+ */
577
+
578
+ it("accepts a name that collides with a Review dialog badge", () => {
579
+ /*
580
+ * "Already-added" is the badge the dialog puts on a host that is already
581
+ * imported. A PTR record can legitimately say exactly that, and the
582
+ * function cannot tell the difference — nor should it try. The dialog
583
+ * must keep the badge distinguishable by POSITION (its own element),
584
+ * never by comparing it against the name text, and this case is the
585
+ * standing reminder that name text can be anything.
586
+ */
587
+ expect(normalizeReverseDnsName("Already-added")).toBe("Already-added");
588
+ });
589
+
590
+ it("accepts a single label at the full 63 characters", () => {
591
+ /*
592
+ * Nothing here clamps for display — MAX_DEVICE_NAME_LENGTH (80) in
593
+ * DiscoveredDeviceBuilder and the `truncate` class in the dialog do that.
594
+ * If this function ever started truncating too, the displayed name and
595
+ * the created name would diverge, which is the bug the dialog was fixed
596
+ * to avoid.
597
+ */
598
+ const longLabel: string =
599
+ "very-long-single-label-".repeat(2) + "a".repeat(17);
600
+ expect(longLabel).toHaveLength(MAX_REVERSE_DNS_LABEL_LENGTH);
601
+ expect(normalizeReverseDnsName(longLabel)).toBe(longLabel);
602
+ });
603
+
604
+ it("accepts a name of only underscores", () => {
605
+ /*
606
+ * Ugly, and real: an underscore is a legal first and last character here
607
+ * precisely because Windows/DHCP-registered names use them. "___" is not
608
+ * a value to invent a special rule for — it slugifies fine (see the slug
609
+ * suite below) and it is still a name somebody published.
610
+ */
611
+ expect(normalizeReverseDnsName("___")).toBe("___");
612
+ expect(normalizeReverseDnsName("_")).toBe("_");
613
+ });
614
+
615
+ it("rejects a name of only hyphens", () => {
616
+ /*
617
+ * The mirror image of the underscore case, and it falls out of the
618
+ * "hyphen may not lead or trail" rule rather than a special case: with
619
+ * every character a hyphen, the first one always leads. Worth pinning
620
+ * because "-" and "--" are what a placeholder or a truncated answer looks
621
+ * like, and either would render as a device named nothing at all.
622
+ */
623
+ expect(normalizeReverseDnsName("-")).toBeUndefined();
624
+ expect(normalizeReverseDnsName("--")).toBeUndefined();
625
+ expect(normalizeReverseDnsName("---")).toBeUndefined();
626
+ expect(normalizeReverseDnsName("-.-")).toBeUndefined();
627
+ });
628
+
629
+ it("treats prototype-shaped values as the ordinary strings they are", () => {
630
+ /*
631
+ * "__proto__", "constructor" and "prototype" are valid DNS labels and are
632
+ * returned verbatim. That is correct AND safe here for a specific reason:
633
+ * this function only ever tests and returns the VALUE, never uses it as
634
+ * an object key, so there is no lookup for a magic name to hijack — the
635
+ * string's destinations are a varchar column and a React text node.
636
+ *
637
+ * The assertion that matters is `toBe`: a returned "__proto__" must be
638
+ * that literal string. If a future implementation routed names through
639
+ * an object map (a cache keyed by name, say), "__proto__" would come back
640
+ * as something else entirely, and this case would catch it.
641
+ *
642
+ * There is deliberately no `typeof ... === "string"` assertion alongside:
643
+ * `toBe("__proto__")` already implies it, so the typeof line could not
644
+ * fail on its own and only made the case look broader than it was.
645
+ */
646
+ expect(normalizeReverseDnsName("__proto__")).toBe("__proto__");
647
+ expect(normalizeReverseDnsName("constructor")).toBe("constructor");
648
+ expect(normalizeReverseDnsName("prototype.example.com")).toBe(
649
+ "prototype.example.com",
650
+ );
651
+ });
652
+ });
653
+
654
+ describe("normalizeReverseDnsName — accepted names are safe to slugify", () => {
655
+ /*
656
+ * NetworkDevice is @SlugifyColumn("name", "slug") with slug varchar(100),
657
+ * so every accepted name here becomes a URL segment without anyone looking
658
+ * at it again. Slug.getSlug strips [&*+~.,\\/()|'"!:@] — which includes the
659
+ * dot, the one character a hostname is made of — and then appends a
660
+ * ten-digit random suffix.
661
+ *
662
+ * The suffix is random but its SHAPE is not, and the regex below rests on
663
+ * that: Faker.getRandomNumbers(10) pushes ten digits each in 1..9 (never 0)
664
+ * and parses the joined string, so no leading zero can ever be dropped and
665
+ * the suffix is always exactly ten digits. Without that guarantee this
666
+ * suite would be intermittently red on roughly one run in ten.
667
+ *
668
+ * Because the suffix means getSlug's return value is never literally empty,
669
+ * a bare "is it non-empty" assertion would be unfalsifiable. So these cases
670
+ * assert on the NAME-DERIVED STEM: the slug must be more than the random
671
+ * suffix, AND it must still contain a recognisable piece of the name. A
672
+ * name that slugified away to nothing would give every such device a slug
673
+ * that is pure entropy, with no trace of what it names.
674
+ */
675
+
676
+ /*
677
+ * [name, the lower-case fragment of it that must survive slugification].
678
+ * The fragment is what a human would use to recognise the device in a URL,
679
+ * and it is written out rather than derived so that a change in getSlug
680
+ * that quietly mangled names has something concrete to fail against.
681
+ */
682
+ const acceptedNames: Array<[string, string]> = [
683
+ ["core-switch-01.corp.example.com", "core-switch-01"],
684
+ ["printer-3", "printer-3"],
685
+ ["ws_finance_12.corp.example.com", "ws_finance_12"],
686
+ ["10-18-166-51.dhcp.corp.example.com", "10-18-166-51"],
687
+ ["SW-Core-01.Corp.Example.COM", "sw-core-01"],
688
+ ["xn--hte-snab.example.com", "hte-snab"],
689
+ ["Already-added", "already-added"],
690
+ ["something.arpa", "somethingarpa"],
691
+ ["51.corp.example.com", "51corpexamplecom"],
692
+ ["notin-addr.arpa", "notin-addrarpa"],
693
+ ["___", "___"],
694
+ ["_", "_"],
695
+ ["a.b", "ab"],
696
+ ["a".repeat(MAX_REVERSE_DNS_LABEL_LENGTH), "a".repeat(20)],
697
+ ];
698
+
699
+ it("leaves a name-derived stem in the slug for every accepted name", () => {
700
+ for (const [name, fragment] of acceptedNames) {
701
+ /*
702
+ * Guard the guard: if the gate stopped accepting one of these, the slug
703
+ * assertion below would be testing a value that never reaches a device.
704
+ */
705
+ expect(normalizeReverseDnsName(name)).toBe(name);
706
+
707
+ const slug: string = Slug.getSlug(name);
708
+
709
+ /*
710
+ * getSlug always appends "-" plus ten digits, so a name that slugified
711
+ * to "" yields exactly "-##########" — which fails this match, because
712
+ * the capture needs at least one character before the suffix.
713
+ */
714
+ const stem: RegExpMatchArray | null = slug.match(/^(.+)-\d{10}$/);
715
+ expect(stem).not.toBeNull();
716
+
717
+ /*
718
+ * And the stem must be THIS name's stem, not merely some non-empty
719
+ * string. The old assertion here was `expect(stem?.[1]).not.toBe("")`,
720
+ * which could not fail in either branch: a matched capture of `.+` is
721
+ * non-empty by construction, and an unmatched one is `undefined`, which
722
+ * also is not "". Comparing against the fragment is what makes the case
723
+ * die if the name stopped reaching the slug.
724
+ */
725
+ expect(stem?.[1]).toContain(fragment);
726
+ }
727
+ });
728
+
729
+ it("does not itself keep a name short enough for the slug column", () => {
730
+ /*
731
+ * The complement of the case above, and the reason MAX_DEVICE_NAME_LENGTH
732
+ * exists: this gate accepts up to 253 characters, which is well past the
733
+ * slug column's varchar(100). Length safety is DiscoveredDeviceBuilder's
734
+ * clamp to 80, not this function's — asserting it here pins where the
735
+ * responsibility lives, so nobody removes the clamp believing the DNS
736
+ * limit already covers it.
737
+ *
738
+ * Asserted on the length of what THIS function returns rather than on
739
+ * Slug.getSlug(...).length, deliberately: the claim is about this gate
740
+ * not truncating, and routing it through Slug would put a foreign
741
+ * module's behaviour (if Slug ever gained a clamp of its own) on the
742
+ * failure path of a suite that does not own it.
743
+ */
744
+ const label: string = "a".repeat(MAX_REVERSE_DNS_LABEL_LENGTH);
745
+ const maximalName: string = `${label}.${label}.${label}.${"b".repeat(61)}`;
746
+ const normalized: string | undefined = normalizeReverseDnsName(maximalName);
747
+ expect(normalized).toBe(maximalName);
748
+ expect(normalized?.length).toBeGreaterThan(100);
749
+ });
750
+ });
751
+
752
+ describe("normalizeReverseDnsName — non-string input", () => {
753
+ /*
754
+ * On the server side the input is read out of a jsonb column, so "the probe
755
+ * sent a number" and "the key is missing" are cases that have to return
756
+ * undefined rather than throw inside a React render. This is the same
757
+ * lesson normalizeDiscoveredHosts learned from a null row in the same
758
+ * column.
759
+ *
760
+ * This block is also what covers re-normalising the `undefined` that a
761
+ * rejected name produces: a later stage feeding its own output back in is
762
+ * exactly `normalizeReverseDnsName(undefined)`, pinned once here rather
763
+ * than re-spelled in a loop that only ever passed it the same value.
764
+ */
765
+
766
+ it("returns undefined for nullish input", () => {
767
+ expect(normalizeReverseDnsName(undefined)).toBeUndefined();
768
+ expect(normalizeReverseDnsName(null)).toBeUndefined();
769
+ });
770
+
771
+ it("returns undefined rather than coercing a number", () => {
772
+ expect(normalizeReverseDnsName(51)).toBeUndefined();
773
+ expect(normalizeReverseDnsName(0)).toBeUndefined();
774
+ expect(normalizeReverseDnsName(NaN)).toBeUndefined();
775
+ });
776
+
777
+ it("returns undefined for booleans, objects and arrays", () => {
778
+ expect(normalizeReverseDnsName(true)).toBeUndefined();
779
+ expect(
780
+ normalizeReverseDnsName({
781
+ toString: () => {
782
+ return "host.example.com";
783
+ },
784
+ }),
785
+ ).toBeUndefined();
786
+ expect(normalizeReverseDnsName(["host.example.com"])).toBeUndefined();
787
+ });
788
+ });
789
+
790
+ describe("normalizeReverseDnsName — idempotence and repeat-call stability", () => {
791
+ /*
792
+ * The value is normalised on the probe, again by normalizeDiscoveredHosts
793
+ * on the way out of the jsonb column, and again by
794
+ * getDiscoveredHostDisplayName at the point of use. Three passes must
795
+ * produce what one pass produced, or the name an operator ticks in the
796
+ * Review dialog and the name the created device gets could differ.
797
+ */
798
+
799
+ it("returns an accepted name unchanged on every later pass", () => {
800
+ /*
801
+ * Each of these CHANGES on the first pass — a root dot dropped, surrounding
802
+ * whitespace trimmed — so the second pass is a real test of convergence
803
+ * rather than a repeat of a no-op.
804
+ */
805
+ const changedByNormalisation: Array<[string, string]> = [
806
+ ["host.example.com.", "host.example.com"],
807
+ [" SW-Core-01.corp.example.com ", "SW-Core-01.corp.example.com"],
808
+ ["\tprinter_2\n", "printer_2"],
809
+ [" 51.corp.example.com. ", "51.corp.example.com"],
810
+ ];
811
+
812
+ for (const [input, expected] of changedByNormalisation) {
813
+ const first: string | undefined = normalizeReverseDnsName(input);
814
+ expect(first).toBe(expected);
815
+
816
+ const second: string | undefined = normalizeReverseDnsName(first);
817
+ const third: string | undefined = normalizeReverseDnsName(second);
818
+
819
+ /*
820
+ * Compared against the expected STRING, not against `first`: comparing
821
+ * the passes to each other would be satisfied by `undefined ===
822
+ * undefined` if a later pass dropped the name entirely.
823
+ */
824
+ expect(second).toBe(expected);
825
+ expect(third).toBe(expected);
826
+ }
827
+ });
828
+
829
+ it("keeps an already-normalised name byte-identical", () => {
830
+ const alreadyNormal: Array<string> = [
831
+ "core-switch-01.corp.example.com",
832
+ "ws_finance_12.corp.example.com",
833
+ "something.arpa",
834
+ "notin-addr.arpa",
835
+ "51.corp.example.com",
836
+ "xn--hte-snab.example.com",
837
+ ];
838
+
839
+ for (const name of alreadyNormal) {
840
+ expect(normalizeReverseDnsName(name)).toBe(name);
841
+ }
842
+ });
843
+
844
+ it("gives the same answer when the same value is passed again", () => {
845
+ /*
846
+ * Purity, asserted by calling the function repeatedly on the IDENTICAL
847
+ * input and requiring the identical answer each time.
848
+ *
849
+ * This is not the tautology it looks like, and it replaces a loop whose
850
+ * second assertion was always `normalizeReverseDnsName(undefined)` — the
851
+ * same call for every input, unable to fail unless the nullish case
852
+ * already had. The mutation this one exists for is a `g` flag on either
853
+ * module-level regex, a one-character edit that reads as harmless
854
+ * housekeeping: `RegExp.prototype.test` with /g advances `lastIndex`, so
855
+ * `/^[0-9]+$/g.test("51")` is true, then false, then true again on
856
+ * successive calls. Under it "51" would be rejected on the probe and
857
+ * ACCEPTED on the server's re-normalisation of the same string — the
858
+ * dialog and the created device disagreeing about a device's name, with
859
+ * nothing to show for it in a single-call test. A memoising cache keyed
860
+ * by name would land here too.
861
+ *
862
+ * Both directions are in the table on purpose: a rejected value must stay
863
+ * rejected across calls, and an accepted one must stay accepted.
864
+ */
865
+ const repeated: Array<[unknown, string | undefined]> = [
866
+ ["51", undefined],
867
+ ["10.18.166.51", undefined],
868
+ ["in-addr.arpa", undefined],
869
+ ["host..example.com", undefined],
870
+ ["a".repeat(MAX_REVERSE_DNS_NAME_LENGTH + 1), undefined],
871
+ ["51.corp.example.com", "51.corp.example.com"],
872
+ ["core-switch-01.corp.example.com", "core-switch-01.corp.example.com"],
873
+ ["___", "___"],
874
+ ];
875
+
876
+ for (const [value, expected] of repeated) {
877
+ expect(normalizeReverseDnsName(value)).toBe(expected);
878
+ expect(normalizeReverseDnsName(value)).toBe(expected);
879
+ expect(normalizeReverseDnsName(value)).toBe(expected);
880
+ }
881
+ });
882
+ });