@oneuptime/common 14.0.9 → 14.0.10
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/AutoRemediationSuggestion.ts +60 -0
- package/Models/DatabaseModels/CephCluster.ts +213 -0
- package/Models/DatabaseModels/DatabaseServer.ts +213 -0
- package/Models/DatabaseModels/DockerHost.ts +213 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
- package/Models/DatabaseModels/GlobalConfig.ts +1 -1
- package/Models/DatabaseModels/Host.ts +213 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/PodmanHost.ts +213 -0
- package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
- package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
- package/Models/DatabaseModels/RunnerJob.ts +144 -0
- package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
- package/Server/API/AutoRemediationAPI.ts +392 -1
- package/Server/API/ResourceAiAccessAPI.ts +1669 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Semaphore.ts +22 -0
- package/Server/Middleware/TelemetryIngest.ts +15 -5
- package/Server/Services/AnalyticsDatabaseService.ts +45 -1
- package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
- package/Server/Services/CephClusterService.ts +109 -1
- package/Server/Services/DatabaseServerService.ts +116 -1
- package/Server/Services/DockerHostService.ts +109 -1
- package/Server/Services/DockerSwarmClusterService.ts +113 -1
- package/Server/Services/HostService.ts +126 -4
- package/Server/Services/MetricRecordingRuleService.ts +47 -0
- package/Server/Services/PodmanHostService.ts +109 -1
- package/Server/Services/ProxmoxClusterService.ts +110 -1
- package/Server/Services/ResourceAiAccessService.ts +1439 -0
- package/Server/Services/ResourceAiAgentJobService.ts +202 -0
- package/Server/Services/ResourceAiAgentService.ts +2432 -0
- package/Server/Services/RunnerJobService.ts +611 -4
- package/Server/Services/TelemetryUsageBillingService.ts +28 -0
- package/Server/Services/TraceRecordingRuleService.ts +33 -0
- package/Server/Services/VMwareVCenterService.ts +110 -1
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1434 -32
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +935 -21
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +873 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +72 -2
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +396 -13
- package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
- package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
- package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +298 -224
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +366 -143
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +93 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
- package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
- package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
- package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
- package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
- package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
- package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
- package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
- package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
- package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
- package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
- package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +63 -5
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +103 -5
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +19 -1
- package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
- package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
- package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
- package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
- package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
- package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
- package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
- package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
- package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
- package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
- package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
- package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
- package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +105 -0
- package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
- package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
- package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
- package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
- package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
- package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
- package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -0
- package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
- package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +722 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +830 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
- package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
- package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +33 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
- package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
- package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
- package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +517 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
- package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
- package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
- package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
- package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
- package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
- package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
- package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
- package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
- package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
- package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
- package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
- package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
- package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
- package/Types/AI/ResourceAiAccessApi.ts +140 -0
- package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
- package/Types/Monitor/RumAlertTemplates.ts +351 -4
- package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
- package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
- package/Types/Runbook/RunbookStepType.ts +29 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +14 -2
- package/Utils/AI/InvestigationReport.ts +121 -15
- package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
- package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
- package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
- package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
- package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
- package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
- package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
- package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
- package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
- package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
- package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
- package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +219 -0
- package/build/dist/Models/DatabaseModels/Host.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/PodmanHost.js +219 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js +145 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +231 -1
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
- package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +12 -5
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +104 -0
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseServerService.js +99 -0
- package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +104 -0
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +114 -2
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +104 -0
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
- package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/RunnerJobService.js +344 -5
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
- package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1029 -30
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +633 -28
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +330 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +600 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +50 -4
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +290 -13
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +183 -209
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +263 -110
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
- package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
- package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +116 -29
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
- package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +13 -2
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/Utils/AI/InvestigationReport.js +71 -11
- package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1060 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Normalizes query text in the output of a `db` command, and masks the
|
|
3
|
+
* credentials and row data only database output carries, before the output
|
|
4
|
+
* leaves the Database AI agent, before the server stores it and before a
|
|
5
|
+
* model reads it. Registered as the "db" hook in ResourceOutputRedactor, so
|
|
6
|
+
* the generic rules still run after it.
|
|
7
|
+
*
|
|
8
|
+
* What a session is running is the most useful thing a database can tell
|
|
9
|
+
* an investigation, and the least safe to pass on verbatim: the literals
|
|
10
|
+
* in a statement are customers' data (emails, card numbers, tokens) and,
|
|
11
|
+
* in an ALTER USER, a password. So, the way pg_stat_statements and
|
|
12
|
+
* performance_schema digests already print statements:
|
|
13
|
+
* - every string literal becomes '?' — '...' with '' and backslash
|
|
14
|
+
* escapes, E'' / B'' / X'' / N'' and MySQL _charset'' prefixes, and
|
|
15
|
+
* PostgreSQL $$...$$ / $tag$...$tag$ bodies ($tag$?$tag$);
|
|
16
|
+
* - every number literal becomes ? — 42, 3.14, 1e9, 0x1F — but never a
|
|
17
|
+
* bind parameter ($1) or a digit inside a name (t1, "col2", `x3`);
|
|
18
|
+
* - a double-quoted word is a PostgreSQL identifier or a MySQL string,
|
|
19
|
+
* and this code cannot know which: it stays only where it can only be
|
|
20
|
+
* a name (next to a dot, or after FROM, JOIN, INTO, UPDATE, TABLE and
|
|
21
|
+
* the like) and becomes "?" everywhere else;
|
|
22
|
+
* - a quoted password after PASSWORD or IDENTIFIED BY / AS is masked
|
|
23
|
+
* wherever it appears, and so are the arguments of a Redis AUTH (and
|
|
24
|
+
* HELLO ... AUTH, MIGRATE ... AUTH / AUTH2), the value after
|
|
25
|
+
* requirepass / masterauth, an ACL SETUSER password, the value of a
|
|
26
|
+
* credential setting listed as name/value, the values of Redis commands
|
|
27
|
+
* that write data (SET, HSET, LPUSH, ... keep the command and the key)
|
|
28
|
+
* and the row bytes SHOW ENGINE INNODB STATUS dumps (hex ...; asc ...).
|
|
29
|
+
*
|
|
30
|
+
* Where query text is found:
|
|
31
|
+
* - a JSON string value under a query-like key (query, info, sql_text,
|
|
32
|
+
* digest_text, ...) or one whose text starts like a statement: decoded,
|
|
33
|
+
* normalized and re-encoded in place;
|
|
34
|
+
* - a JSON array that is a Redis command (["AUTH", "..."], ["SET", "k",
|
|
35
|
+
* "v"]): element by element;
|
|
36
|
+
* - in plain text, from a statement keyword at the start of a field (line
|
|
37
|
+
* start, after ": ", "= ", "| ", a tab or a column gap) to the end of the
|
|
38
|
+
* field (end of line, a tab or " | "), and lines that continue a
|
|
39
|
+
* statement (starting with WHERE, AND, FROM, VALUES, ...);
|
|
40
|
+
* - a single-quoted literal anywhere in plain text (an apostrophe inside
|
|
41
|
+
* a word, as in "don't", is not one).
|
|
42
|
+
* The executor makes this exact by printing query text in a JSON string
|
|
43
|
+
* field (or last on its line) and Redis command arguments as a JSON array.
|
|
44
|
+
*
|
|
45
|
+
* Over-masking is accepted; under-masking is not. Total: anything that is
|
|
46
|
+
* not text becomes "", and if normalizing ever failed the whole output
|
|
47
|
+
* would be withheld rather than passed on unnormalized.
|
|
48
|
+
*
|
|
49
|
+
* Part of the import-closed resource policy directory that the resource AI
|
|
50
|
+
* agent carries a byte-identical copy of: relative imports of that set only.
|
|
51
|
+
*/
|
|
52
|
+
// What a string or number literal becomes (inside its quotes for a string).
|
|
53
|
+
export const DATABASE_LITERAL_PLACEHOLDER = "?";
|
|
54
|
+
// What a credential becomes; the same marker the generic rules write.
|
|
55
|
+
export const DATABASE_REDACTED_MARKER = "[redacted]";
|
|
56
|
+
// The whole output, when it could not be normalized safely.
|
|
57
|
+
export const DATABASE_UNREDACTABLE_OUTPUT = "[redacted: the database output could not be normalized, so it is withheld]";
|
|
58
|
+
// Words that start a statement.
|
|
59
|
+
const STATEMENT_KEYWORDS = new Set([
|
|
60
|
+
"SELECT",
|
|
61
|
+
"INSERT",
|
|
62
|
+
"UPDATE",
|
|
63
|
+
"DELETE",
|
|
64
|
+
"WITH",
|
|
65
|
+
"MERGE",
|
|
66
|
+
"UPSERT",
|
|
67
|
+
"REPLACE",
|
|
68
|
+
"CALL",
|
|
69
|
+
"EXEC",
|
|
70
|
+
"EXECUTE",
|
|
71
|
+
"EXPLAIN",
|
|
72
|
+
"SET",
|
|
73
|
+
"SHOW",
|
|
74
|
+
"CREATE",
|
|
75
|
+
"ALTER",
|
|
76
|
+
"DROP",
|
|
77
|
+
"TRUNCATE",
|
|
78
|
+
"GRANT",
|
|
79
|
+
"REVOKE",
|
|
80
|
+
"COPY",
|
|
81
|
+
"VALUES",
|
|
82
|
+
"BEGIN",
|
|
83
|
+
"COMMIT",
|
|
84
|
+
"ROLLBACK",
|
|
85
|
+
"DECLARE",
|
|
86
|
+
"FETCH",
|
|
87
|
+
"PREPARE",
|
|
88
|
+
"DEALLOCATE",
|
|
89
|
+
"DO",
|
|
90
|
+
"LOCK",
|
|
91
|
+
"VACUUM",
|
|
92
|
+
"ANALYZE",
|
|
93
|
+
"ANALYSE",
|
|
94
|
+
"REINDEX",
|
|
95
|
+
"REFRESH",
|
|
96
|
+
"NOTIFY",
|
|
97
|
+
"OPTIMIZE",
|
|
98
|
+
"KILL",
|
|
99
|
+
"FLUSH",
|
|
100
|
+
"RENAME",
|
|
101
|
+
"HANDLER",
|
|
102
|
+
"LOAD",
|
|
103
|
+
]);
|
|
104
|
+
// Words a line (or a field) that continues a statement starts with.
|
|
105
|
+
const CONTINUATION_KEYWORDS = new Set([
|
|
106
|
+
"FROM",
|
|
107
|
+
"WHERE",
|
|
108
|
+
"AND",
|
|
109
|
+
"OR",
|
|
110
|
+
"NOT",
|
|
111
|
+
"JOIN",
|
|
112
|
+
"INNER",
|
|
113
|
+
"LEFT",
|
|
114
|
+
"RIGHT",
|
|
115
|
+
"FULL",
|
|
116
|
+
"CROSS",
|
|
117
|
+
"NATURAL",
|
|
118
|
+
"OUTER",
|
|
119
|
+
"ON",
|
|
120
|
+
"GROUP",
|
|
121
|
+
"ORDER",
|
|
122
|
+
"HAVING",
|
|
123
|
+
"LIMIT",
|
|
124
|
+
"OFFSET",
|
|
125
|
+
"UNION",
|
|
126
|
+
"INTERSECT",
|
|
127
|
+
"EXCEPT",
|
|
128
|
+
"RETURNING",
|
|
129
|
+
"VALUES",
|
|
130
|
+
"SET",
|
|
131
|
+
"WHEN",
|
|
132
|
+
"THEN",
|
|
133
|
+
"ELSE",
|
|
134
|
+
"CASE",
|
|
135
|
+
"INTO",
|
|
136
|
+
"USING",
|
|
137
|
+
"WINDOW",
|
|
138
|
+
"FETCH",
|
|
139
|
+
"FOR",
|
|
140
|
+
"LATERAL",
|
|
141
|
+
]);
|
|
142
|
+
/*
|
|
143
|
+
* Words after which a double-quoted word can only be a name. `AS` and `BY`
|
|
144
|
+
* are left out on purpose: in IDENTIFIED WITH plugin AS "hash" and
|
|
145
|
+
* IDENTIFIED BY "password" the word is a MySQL string.
|
|
146
|
+
*/
|
|
147
|
+
const IDENTIFIER_CONTEXT_KEYWORDS = new Set([
|
|
148
|
+
"FROM",
|
|
149
|
+
"JOIN",
|
|
150
|
+
"INTO",
|
|
151
|
+
"UPDATE",
|
|
152
|
+
"TABLE",
|
|
153
|
+
"INDEX",
|
|
154
|
+
"VIEW",
|
|
155
|
+
"ON",
|
|
156
|
+
"EXISTS",
|
|
157
|
+
"ONLY",
|
|
158
|
+
"SCHEMA",
|
|
159
|
+
"SEQUENCE",
|
|
160
|
+
"TRIGGER",
|
|
161
|
+
"FUNCTION",
|
|
162
|
+
"PROCEDURE",
|
|
163
|
+
"TYPE",
|
|
164
|
+
"COLUMN",
|
|
165
|
+
"CONSTRAINT",
|
|
166
|
+
"REFERENCES",
|
|
167
|
+
"DATABASE",
|
|
168
|
+
"EXTENSION",
|
|
169
|
+
]);
|
|
170
|
+
// JSON keys whose string value is statement text (compared lowercase).
|
|
171
|
+
const QUERY_KEYS = new Set([
|
|
172
|
+
"query",
|
|
173
|
+
"queries",
|
|
174
|
+
"info",
|
|
175
|
+
"sql",
|
|
176
|
+
"sql_text",
|
|
177
|
+
"sqltext",
|
|
178
|
+
"digest_text",
|
|
179
|
+
"query_text",
|
|
180
|
+
"querytext",
|
|
181
|
+
"query_sample_text",
|
|
182
|
+
"sample_query",
|
|
183
|
+
"example_query",
|
|
184
|
+
"normalized_query",
|
|
185
|
+
"statement",
|
|
186
|
+
"stmt",
|
|
187
|
+
"current_query",
|
|
188
|
+
"last_query",
|
|
189
|
+
"last_statement",
|
|
190
|
+
]);
|
|
191
|
+
/*
|
|
192
|
+
* Redis commands whose arguments after the key are data: only the command
|
|
193
|
+
* and the key stay.
|
|
194
|
+
*/
|
|
195
|
+
const REDIS_DATA_WRITE_COMMANDS = new Set([
|
|
196
|
+
"set",
|
|
197
|
+
"setex",
|
|
198
|
+
"psetex",
|
|
199
|
+
"setnx",
|
|
200
|
+
"getset",
|
|
201
|
+
"getex",
|
|
202
|
+
"append",
|
|
203
|
+
"setrange",
|
|
204
|
+
"mset",
|
|
205
|
+
"msetnx",
|
|
206
|
+
"hset",
|
|
207
|
+
"hmset",
|
|
208
|
+
"hsetnx",
|
|
209
|
+
"lpush",
|
|
210
|
+
"rpush",
|
|
211
|
+
"lpushx",
|
|
212
|
+
"rpushx",
|
|
213
|
+
"lset",
|
|
214
|
+
"linsert",
|
|
215
|
+
"lrem",
|
|
216
|
+
"sadd",
|
|
217
|
+
"srem",
|
|
218
|
+
"sismember",
|
|
219
|
+
"smismember",
|
|
220
|
+
"zadd",
|
|
221
|
+
"zrem",
|
|
222
|
+
"zincrby",
|
|
223
|
+
"xadd",
|
|
224
|
+
"geoadd",
|
|
225
|
+
"pfadd",
|
|
226
|
+
"publish",
|
|
227
|
+
"spublish",
|
|
228
|
+
"eval",
|
|
229
|
+
"evalsha",
|
|
230
|
+
"eval_ro",
|
|
231
|
+
"evalsha_ro",
|
|
232
|
+
"fcall",
|
|
233
|
+
"fcall_ro",
|
|
234
|
+
"restore",
|
|
235
|
+
"json.set",
|
|
236
|
+
// Later commands that carry values (or members) after the key.
|
|
237
|
+
"hsetex",
|
|
238
|
+
"smove",
|
|
239
|
+
"lpos",
|
|
240
|
+
"zscore",
|
|
241
|
+
"zmscore",
|
|
242
|
+
"zrank",
|
|
243
|
+
"zrevrank",
|
|
244
|
+
"json.mset",
|
|
245
|
+
"json.merge",
|
|
246
|
+
"json.arrappend",
|
|
247
|
+
"json.arrinsert",
|
|
248
|
+
"json.arrindex",
|
|
249
|
+
"json.strappend",
|
|
250
|
+
"bf.add",
|
|
251
|
+
"bf.madd",
|
|
252
|
+
"bf.exists",
|
|
253
|
+
"bf.mexists",
|
|
254
|
+
"bf.insert",
|
|
255
|
+
"cf.add",
|
|
256
|
+
"cf.addnx",
|
|
257
|
+
"cf.exists",
|
|
258
|
+
"cf.insert",
|
|
259
|
+
]);
|
|
260
|
+
// A setting name whose value is a credential (Valkey names masterauth primaryauth).
|
|
261
|
+
const CREDENTIAL_SETTING_NAME_REGEX = /pass|pwd|secret|token|credential|masterauth|primaryauth|auth$/i;
|
|
262
|
+
// The Redis / Valkey settings whose value is a password.
|
|
263
|
+
const REDIS_CREDENTIAL_SETTINGS = new Set([
|
|
264
|
+
"requirepass",
|
|
265
|
+
"masterauth",
|
|
266
|
+
"primaryauth",
|
|
267
|
+
]);
|
|
268
|
+
// A CONFIG SET parameter whose value is (or may be) a credential; compared lowercase.
|
|
269
|
+
const REDIS_SECRET_PARAMETER_REGEX = /pass|auth|secret|token|key|cred|user/;
|
|
270
|
+
// An ACL SETUSER rule that sets or removes a password (>pw, <pw) or its hash (#h, !h).
|
|
271
|
+
const ACL_PASSWORD_RULE_REGEX = /^[<>#!]/;
|
|
272
|
+
const TRAILING_WHITESPACE_REGEX = /\s*$/;
|
|
273
|
+
const ASCII_WORD_CHAR_REGEX = /[A-Za-z0-9_$]/;
|
|
274
|
+
const ASCII_WORD_START_REGEX = /[A-Za-z_]/;
|
|
275
|
+
const DIGIT_REGEX = /[0-9]/;
|
|
276
|
+
const WHITESPACE_REGEX = /\s/;
|
|
277
|
+
// A literal's prefix: E'', B'', X'', N'' and MySQL's _charset''.
|
|
278
|
+
const STRING_PREFIX_REGEX = /(?:^|[^A-Za-z0-9_$])(?:[EeBbXxNn]|_[A-Za-z0-9]+)$/;
|
|
279
|
+
const DOLLAR_TAG_REGEX = /\$(?:[A-Za-z_][A-Za-z0-9_]*)?\$/y;
|
|
280
|
+
const NUMBER_REGEX = /0[xX][0-9A-Fa-f]+|[0-9]+(?:\.[0-9]*)?(?:[eE][+-]?[0-9]+)?|\.[0-9]+(?:[eE][+-]?[0-9]+)?/y;
|
|
281
|
+
const LETTERS_REGEX = /[A-Za-z_]+/y;
|
|
282
|
+
// A letter of a name: ASCII word characters, and any non-ASCII character.
|
|
283
|
+
function isWordChar(ch) {
|
|
284
|
+
return (ch !== "" && (ch.charCodeAt(0) > 127 || ASCII_WORD_CHAR_REGEX.test(ch)));
|
|
285
|
+
}
|
|
286
|
+
function isWordStart(ch) {
|
|
287
|
+
return (ch !== "" && (ch.charCodeAt(0) > 127 || ASCII_WORD_START_REGEX.test(ch)));
|
|
288
|
+
}
|
|
289
|
+
function isDigit(ch) {
|
|
290
|
+
return ch !== "" && DIGIT_REGEX.test(ch);
|
|
291
|
+
}
|
|
292
|
+
function isWhitespace(ch) {
|
|
293
|
+
return ch !== "" && WHITESPACE_REGEX.test(ch);
|
|
294
|
+
}
|
|
295
|
+
// Already masked (by an earlier pass or an earlier run): kept, and not counted again.
|
|
296
|
+
function isPlaceholder(content) {
|
|
297
|
+
return (content === DATABASE_LITERAL_PLACEHOLDER ||
|
|
298
|
+
content === DATABASE_REDACTED_MARKER);
|
|
299
|
+
}
|
|
300
|
+
/*
|
|
301
|
+
* Where the single-quoted literal opening at `start` ends: the index after
|
|
302
|
+
* its closing quote, or the end of the text when it never closes. A doubled
|
|
303
|
+
* '' and a backslash both escape. When a closing quote is glued to a word
|
|
304
|
+
* (`'a\' , 'secret'` read the MySQL way ends right before "secret"), the
|
|
305
|
+
* backslash was a PostgreSQL literal one, so the literal runs on to the next
|
|
306
|
+
* quote that ends a word — the longer reading, which masks both.
|
|
307
|
+
*/
|
|
308
|
+
function scanSingleQuoted(text, start) {
|
|
309
|
+
let index = start + 1;
|
|
310
|
+
while (index < text.length) {
|
|
311
|
+
const ch = text.charAt(index);
|
|
312
|
+
if (ch === "\\" && index + 1 < text.length) {
|
|
313
|
+
index += 2;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (ch === "'") {
|
|
317
|
+
if (text.charAt(index + 1) === "'") {
|
|
318
|
+
index += 2;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (isWordChar(text.charAt(index + 1))) {
|
|
322
|
+
index++;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
return index + 1;
|
|
326
|
+
}
|
|
327
|
+
index++;
|
|
328
|
+
}
|
|
329
|
+
return text.length;
|
|
330
|
+
}
|
|
331
|
+
/*
|
|
332
|
+
* Where a double-quoted (or backtick-quoted) word opening at `start` ends:
|
|
333
|
+
* a doubled quote escapes, and so does a backslash inside double quotes.
|
|
334
|
+
*/
|
|
335
|
+
function scanQuoted(text, start, quote) {
|
|
336
|
+
let index = start + 1;
|
|
337
|
+
while (index < text.length) {
|
|
338
|
+
const ch = text.charAt(index);
|
|
339
|
+
if (ch === "\\" && quote === '"' && index + 1 < text.length) {
|
|
340
|
+
index += 2;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (ch === quote) {
|
|
344
|
+
if (text.charAt(index + 1) === quote) {
|
|
345
|
+
index += 2;
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
return index + 1;
|
|
349
|
+
}
|
|
350
|
+
index++;
|
|
351
|
+
}
|
|
352
|
+
return text.length;
|
|
353
|
+
}
|
|
354
|
+
// Did the quoted run from `start` to `end` close with `quote`?
|
|
355
|
+
function isClosed(text, start, end, quote) {
|
|
356
|
+
return end - start >= 2 && text.charAt(end - 1) === quote;
|
|
357
|
+
}
|
|
358
|
+
// The last word already written (uppercased) and the last non-space character.
|
|
359
|
+
function lastWordOf(out) {
|
|
360
|
+
let end = out.length;
|
|
361
|
+
while (end > 0 && isWhitespace(out.charAt(end - 1))) {
|
|
362
|
+
end--;
|
|
363
|
+
}
|
|
364
|
+
const lastChar = end > 0 ? out.charAt(end - 1) : "";
|
|
365
|
+
let begin = end;
|
|
366
|
+
while (begin > 0 && isWordChar(out.charAt(begin - 1))) {
|
|
367
|
+
begin--;
|
|
368
|
+
}
|
|
369
|
+
return { word: out.slice(begin, end).toUpperCase(), lastChar };
|
|
370
|
+
}
|
|
371
|
+
// Can the double-quoted word ending at `end` only be a name?
|
|
372
|
+
function isIdentifierPosition(out, text, end) {
|
|
373
|
+
if (text.charAt(end) === ".") {
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
const previous = lastWordOf(out);
|
|
377
|
+
return (previous.lastChar === "." || IDENTIFIER_CONTEXT_KEYWORDS.has(previous.word));
|
|
378
|
+
}
|
|
379
|
+
/*
|
|
380
|
+
* Normalize the statement starting at `start`. With `stopAtColumn` (plain
|
|
381
|
+
* text output) it stops at a tab or " | " outside quotes: the next column
|
|
382
|
+
* of a table, not more of the statement.
|
|
383
|
+
*/
|
|
384
|
+
function lexStatement(text, start, stopAtColumn) {
|
|
385
|
+
let out = "";
|
|
386
|
+
let count = 0;
|
|
387
|
+
let index = start;
|
|
388
|
+
while (index < text.length) {
|
|
389
|
+
const ch = text.charAt(index);
|
|
390
|
+
if (stopAtColumn &&
|
|
391
|
+
(ch === "\t" || (ch === " " && text.startsWith(" | ", index)))) {
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
if (ch === "'") {
|
|
395
|
+
const end = scanSingleQuoted(text, index);
|
|
396
|
+
const closed = isClosed(text, index, end, "'");
|
|
397
|
+
if (closed && isPlaceholder(text.slice(index + 1, end - 1))) {
|
|
398
|
+
out += text.slice(index, end);
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
out += `'${DATABASE_LITERAL_PLACEHOLDER}'`;
|
|
402
|
+
count++;
|
|
403
|
+
}
|
|
404
|
+
index = end;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (ch === '"') {
|
|
408
|
+
const end = scanQuoted(text, index, '"');
|
|
409
|
+
const closed = isClosed(text, index, end, '"');
|
|
410
|
+
if (closed &&
|
|
411
|
+
(isPlaceholder(text.slice(index + 1, end - 1)) ||
|
|
412
|
+
isIdentifierPosition(out, text, end))) {
|
|
413
|
+
out += text.slice(index, end);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
out += `"${DATABASE_LITERAL_PLACEHOLDER}"`;
|
|
417
|
+
count++;
|
|
418
|
+
}
|
|
419
|
+
index = end;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (ch === "`") {
|
|
423
|
+
const end = scanQuoted(text, index, "`");
|
|
424
|
+
out += text.slice(index, end);
|
|
425
|
+
index = end;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (ch === "$") {
|
|
429
|
+
DOLLAR_TAG_REGEX.lastIndex = index;
|
|
430
|
+
const tagMatch = DOLLAR_TAG_REGEX.exec(text);
|
|
431
|
+
if (tagMatch) {
|
|
432
|
+
const tag = tagMatch[0];
|
|
433
|
+
const close = text.indexOf(tag, index + tag.length);
|
|
434
|
+
if (close >= 0 &&
|
|
435
|
+
isPlaceholder(text.slice(index + tag.length, close))) {
|
|
436
|
+
out += text.slice(index, close + tag.length);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
out += `${tag}${DATABASE_LITERAL_PLACEHOLDER}${close >= 0 ? tag : ""}`;
|
|
440
|
+
count++;
|
|
441
|
+
}
|
|
442
|
+
index = close >= 0 ? close + tag.length : text.length;
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
// $1: a bind parameter, kept whole.
|
|
446
|
+
let end = index + 1;
|
|
447
|
+
while (isDigit(text.charAt(end))) {
|
|
448
|
+
end++;
|
|
449
|
+
}
|
|
450
|
+
out += text.slice(index, end);
|
|
451
|
+
index = end;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (isWordStart(ch)) {
|
|
455
|
+
let end = index + 1;
|
|
456
|
+
while (end < text.length && isWordChar(text.charAt(end))) {
|
|
457
|
+
end++;
|
|
458
|
+
}
|
|
459
|
+
out += text.slice(index, end);
|
|
460
|
+
index = end;
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (isDigit(ch) ||
|
|
464
|
+
(ch === "." &&
|
|
465
|
+
isDigit(text.charAt(index + 1)) &&
|
|
466
|
+
!isWordChar(text.charAt(index - 1)))) {
|
|
467
|
+
NUMBER_REGEX.lastIndex = index;
|
|
468
|
+
const number = NUMBER_REGEX.exec(text);
|
|
469
|
+
let end = number ? index + number[0].length : index + 1;
|
|
470
|
+
if (isWordChar(text.charAt(end))) {
|
|
471
|
+
// A name that starts with digits (MySQL allows 1col): kept whole.
|
|
472
|
+
while (end < text.length && isWordChar(text.charAt(end))) {
|
|
473
|
+
end++;
|
|
474
|
+
}
|
|
475
|
+
out += text.slice(index, end);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
out += DATABASE_LITERAL_PLACEHOLDER;
|
|
479
|
+
count++;
|
|
480
|
+
}
|
|
481
|
+
index = end;
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
out += ch;
|
|
485
|
+
index++;
|
|
486
|
+
}
|
|
487
|
+
return { out, count, end: index };
|
|
488
|
+
}
|
|
489
|
+
/*
|
|
490
|
+
* Normalize one statement's text: string and number literals become ?, a
|
|
491
|
+
* double-quoted word that could be a string becomes "?". Pure and total.
|
|
492
|
+
*/
|
|
493
|
+
export function normalizeSqlText(sql) {
|
|
494
|
+
if (typeof sql !== "string" || !sql) {
|
|
495
|
+
return { text: "", redactionCount: 0 };
|
|
496
|
+
}
|
|
497
|
+
const lexed = lexStatement(sql, 0, false);
|
|
498
|
+
return { text: lexed.out, redactionCount: lexed.count };
|
|
499
|
+
}
|
|
500
|
+
// normalizeSqlText, the text only: for an executor that normalizes a query column itself.
|
|
501
|
+
export function redactDatabaseQueryText(sql) {
|
|
502
|
+
return normalizeSqlText(sql).text;
|
|
503
|
+
}
|
|
504
|
+
// Skip whitespace, "(" and comments before the first word of a statement.
|
|
505
|
+
function statementStart(text) {
|
|
506
|
+
let index = 0;
|
|
507
|
+
while (index < text.length) {
|
|
508
|
+
const ch = text.charAt(index);
|
|
509
|
+
if (isWhitespace(ch) || ch === "(") {
|
|
510
|
+
index++;
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
if (text.startsWith("/*", index)) {
|
|
514
|
+
const close = text.indexOf("*/", index + 2);
|
|
515
|
+
index = close >= 0 ? close + 2 : text.length;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (text.startsWith("--", index)) {
|
|
519
|
+
const close = text.indexOf("\n", index);
|
|
520
|
+
index = close >= 0 ? close + 1 : text.length;
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
return index;
|
|
526
|
+
}
|
|
527
|
+
// The keyword at `index`, uppercased, when a whole word stands there; "" otherwise.
|
|
528
|
+
function keywordAt(text, index) {
|
|
529
|
+
if (index > 0 && isWordChar(text.charAt(index - 1))) {
|
|
530
|
+
return "";
|
|
531
|
+
}
|
|
532
|
+
LETTERS_REGEX.lastIndex = index;
|
|
533
|
+
const match = LETTERS_REGEX.exec(text);
|
|
534
|
+
if (!match) {
|
|
535
|
+
return "";
|
|
536
|
+
}
|
|
537
|
+
const after = text.charAt(index + match[0].length);
|
|
538
|
+
// "SELECT", "SELECT(", "SELECT*", "BEGIN;" — never "selection" or "set_config".
|
|
539
|
+
if (after !== "" && !isWhitespace(after) && !"(;*".includes(after)) {
|
|
540
|
+
return "";
|
|
541
|
+
}
|
|
542
|
+
return match[0].toUpperCase();
|
|
543
|
+
}
|
|
544
|
+
// Does this text start (after whitespace, "(" and comments) like a statement?
|
|
545
|
+
export function looksLikeSqlStatement(text) {
|
|
546
|
+
if (typeof text !== "string" || !text) {
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
return STATEMENT_KEYWORDS.has(keywordAt(text, statementStart(text)));
|
|
550
|
+
}
|
|
551
|
+
// Decode a JSON string body; null when it is not valid JSON.
|
|
552
|
+
function decodeJsonString(body) {
|
|
553
|
+
try {
|
|
554
|
+
const decoded = JSON.parse(`"${body}"`);
|
|
555
|
+
return typeof decoded === "string" ? decoded : null;
|
|
556
|
+
}
|
|
557
|
+
catch (_a) {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/*
|
|
562
|
+
* The JSON string opening at `start`: backslash escapes only, and never
|
|
563
|
+
* past a line break (JSON strings hold none). When one does not close
|
|
564
|
+
* before the line ends, no later quote on that line can close one either
|
|
565
|
+
* (every quote after it is escaped, and a scan from an escaped quote pairs
|
|
566
|
+
* the backslashes after it the same way), so callers skip to the next line:
|
|
567
|
+
* the scans stay linear on hostile text.
|
|
568
|
+
*/
|
|
569
|
+
function scanJsonString(text, start) {
|
|
570
|
+
let index = start + 1;
|
|
571
|
+
while (index < text.length) {
|
|
572
|
+
const ch = text.charAt(index);
|
|
573
|
+
if (ch === "\\") {
|
|
574
|
+
if (index + 1 >= text.length || text.charAt(index + 1) === "\n") {
|
|
575
|
+
return { end: index + 1, closed: false };
|
|
576
|
+
}
|
|
577
|
+
index += 2;
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
if (ch === "\n") {
|
|
581
|
+
return { end: index, closed: false };
|
|
582
|
+
}
|
|
583
|
+
if (ch === '"') {
|
|
584
|
+
return { end: index + 1, closed: true };
|
|
585
|
+
}
|
|
586
|
+
index++;
|
|
587
|
+
}
|
|
588
|
+
return { end: text.length, closed: false };
|
|
589
|
+
}
|
|
590
|
+
/*
|
|
591
|
+
* JSON string values that hold statement text: under a query-like key, or
|
|
592
|
+
* starting like a statement. Decoded, normalized and re-encoded in place;
|
|
593
|
+
* keys and every other string stay byte for byte.
|
|
594
|
+
*/
|
|
595
|
+
function maskJsonStatementStrings(text) {
|
|
596
|
+
let out = "";
|
|
597
|
+
let cursor = 0;
|
|
598
|
+
let count = 0;
|
|
599
|
+
let index = 0;
|
|
600
|
+
// Where the last query-like key's colon ended, while its value may follow.
|
|
601
|
+
let queryKeyEnd = -1;
|
|
602
|
+
while (index < text.length) {
|
|
603
|
+
const quote = text.indexOf('"', index);
|
|
604
|
+
if (quote < 0) {
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
const scan = scanJsonString(text, quote);
|
|
608
|
+
if (!scan.closed) {
|
|
609
|
+
index = scan.end + 1;
|
|
610
|
+
queryKeyEnd = -1;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
const body = text.slice(quote + 1, scan.end - 1);
|
|
614
|
+
let after = scan.end;
|
|
615
|
+
while (isWhitespace(text.charAt(after))) {
|
|
616
|
+
after++;
|
|
617
|
+
}
|
|
618
|
+
if (text.charAt(after) === ":") {
|
|
619
|
+
const key = decodeJsonString(body);
|
|
620
|
+
queryKeyEnd =
|
|
621
|
+
key !== null && QUERY_KEYS.has(key.toLowerCase()) ? after + 1 : -1;
|
|
622
|
+
index = after + 1;
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
const isQueryValue = queryKeyEnd >= 0 && !text.slice(queryKeyEnd, quote).trim();
|
|
626
|
+
queryKeyEnd = -1;
|
|
627
|
+
index = scan.end;
|
|
628
|
+
const decoded = decodeJsonString(body);
|
|
629
|
+
const value = decoded === null ? body : decoded;
|
|
630
|
+
if (!isQueryValue && !looksLikeSqlStatement(value)) {
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
const normalized = normalizeSqlText(value);
|
|
634
|
+
if (normalized.redactionCount === 0) {
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
count += normalized.redactionCount;
|
|
638
|
+
out += text.slice(cursor, quote);
|
|
639
|
+
out +=
|
|
640
|
+
decoded === null
|
|
641
|
+
? `"${normalized.text.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`
|
|
642
|
+
: JSON.stringify(normalized.text);
|
|
643
|
+
cursor = scan.end;
|
|
644
|
+
}
|
|
645
|
+
out += text.slice(cursor);
|
|
646
|
+
return { text: out, redactionCount: count };
|
|
647
|
+
}
|
|
648
|
+
/*
|
|
649
|
+
* A credential setting listed as a name/value object — {"name":
|
|
650
|
+
* "requirepass", "value": "..."} or pg_settings' {"name": ..., "setting":
|
|
651
|
+
* ...}: the property right after the name is masked, whatever it is called.
|
|
652
|
+
*/
|
|
653
|
+
const JSON_CREDENTIAL_SETTING_REGEX = /("(?:name|variable_name|parameter|param|key|setting_name)"\s*:\s*"((?:[^"\\\n]|\\.)*)"\s*,\s*"[A-Za-z_]+"\s*:\s*")((?:[^"\\\n]|\\.)*)(")/gi;
|
|
654
|
+
function maskJsonCredentialSettings(text) {
|
|
655
|
+
let count = 0;
|
|
656
|
+
const masked = text.replace(JSON_CREDENTIAL_SETTING_REGEX, (whole, prefix, name, value, suffix) => {
|
|
657
|
+
if (!CREDENTIAL_SETTING_NAME_REGEX.test(name) ||
|
|
658
|
+
!value ||
|
|
659
|
+
isPlaceholder(value)) {
|
|
660
|
+
return whole;
|
|
661
|
+
}
|
|
662
|
+
count++;
|
|
663
|
+
return `${prefix}${DATABASE_REDACTED_MARKER}${suffix}`;
|
|
664
|
+
});
|
|
665
|
+
return { text: masked, redactionCount: count };
|
|
666
|
+
}
|
|
667
|
+
// Which arguments of a Redis command (args[0] is the command) are secrets or data.
|
|
668
|
+
function redisMaskedIndexes(args) {
|
|
669
|
+
const lower = args.map((arg) => {
|
|
670
|
+
return arg.toLowerCase();
|
|
671
|
+
});
|
|
672
|
+
const command = lower[0] || "";
|
|
673
|
+
const indexes = [];
|
|
674
|
+
const maskFrom = (from) => {
|
|
675
|
+
for (let index = from; index < args.length; index++) {
|
|
676
|
+
indexes.push(index);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
if (command === "auth") {
|
|
680
|
+
maskFrom(1);
|
|
681
|
+
}
|
|
682
|
+
else if (command === "hello" || command === "migrate") {
|
|
683
|
+
for (let index = 1; index < args.length; index++) {
|
|
684
|
+
if (lower[index] === "auth") {
|
|
685
|
+
indexes.push(index + 1);
|
|
686
|
+
// HELLO protover AUTH username password
|
|
687
|
+
if (command === "hello") {
|
|
688
|
+
indexes.push(index + 2);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
else if (lower[index] === "auth2") {
|
|
692
|
+
indexes.push(index + 1, index + 2);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
else if (command === "config" && lower[1] === "set") {
|
|
697
|
+
for (let index = 2; index + 1 < args.length; index += 2) {
|
|
698
|
+
if (REDIS_SECRET_PARAMETER_REGEX.test(lower[index] || "")) {
|
|
699
|
+
indexes.push(index + 1);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
else if (command === "acl" && lower[1] === "setuser") {
|
|
704
|
+
for (let index = 3; index < args.length; index++) {
|
|
705
|
+
if (ACL_PASSWORD_RULE_REGEX.test(args[index] || "")) {
|
|
706
|
+
indexes.push(index);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
else if (REDIS_DATA_WRITE_COMMANDS.has(command)) {
|
|
711
|
+
maskFrom(2);
|
|
712
|
+
}
|
|
713
|
+
// A credential setting followed by its value, wherever it sits (a CONFIG GET reply).
|
|
714
|
+
for (let index = 0; index + 1 < args.length; index++) {
|
|
715
|
+
if (REDIS_CREDENTIAL_SETTINGS.has(lower[index] || "")) {
|
|
716
|
+
indexes.push(index + 1);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return indexes.filter((index) => {
|
|
720
|
+
return index > 0 && index < args.length;
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
/*
|
|
724
|
+
* A Redis command's arguments (args[0] is the command) with its secret and
|
|
725
|
+
* data arguments replaced by the redaction marker — the same arguments the
|
|
726
|
+
* output hook masks in a JSON array. For an executor that prints a command
|
|
727
|
+
* it read from the server (SLOWLOG GET): masked before anything can cut
|
|
728
|
+
* the array short (an unclosed element is never masked) or encode it as a
|
|
729
|
+
* JSON string inside a JSON document (where no array is seen at all).
|
|
730
|
+
*/
|
|
731
|
+
export function redactRedisCommandArguments(args) {
|
|
732
|
+
const words = (Array.isArray(args) ? args : []).map((arg) => {
|
|
733
|
+
return typeof arg === "string" ? arg : String(arg !== null && arg !== void 0 ? arg : "");
|
|
734
|
+
});
|
|
735
|
+
const masked = redisMaskedIndexes(words);
|
|
736
|
+
return words.map((word, index) => {
|
|
737
|
+
return masked.includes(index) && !isPlaceholder(word)
|
|
738
|
+
? DATABASE_REDACTED_MARKER
|
|
739
|
+
: word;
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
const JSON_ARRAY_START_REGEX = /\[\s*"/g;
|
|
743
|
+
/*
|
|
744
|
+
* JSON arrays of strings that are a Redis command (SLOWLOG entries, a
|
|
745
|
+
* CONFIG GET reply): secret and data arguments are masked in place.
|
|
746
|
+
*/
|
|
747
|
+
function maskRedisCommandArrays(text) {
|
|
748
|
+
let out = "";
|
|
749
|
+
let cursor = 0;
|
|
750
|
+
let count = 0;
|
|
751
|
+
JSON_ARRAY_START_REGEX.lastIndex = 0;
|
|
752
|
+
let match = JSON_ARRAY_START_REGEX.exec(text);
|
|
753
|
+
while (match) {
|
|
754
|
+
const elements = [];
|
|
755
|
+
let index = match.index + 1;
|
|
756
|
+
while (index < text.length) {
|
|
757
|
+
while (isWhitespace(text.charAt(index))) {
|
|
758
|
+
index++;
|
|
759
|
+
}
|
|
760
|
+
if (text.charAt(index) !== '"') {
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
const scan = scanJsonString(text, index);
|
|
764
|
+
if (!scan.closed) {
|
|
765
|
+
// Nothing later on this line can close a string: go on from the next line.
|
|
766
|
+
index = scan.end + 1;
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
const end = scan.end;
|
|
770
|
+
const body = text.slice(index + 1, end - 1);
|
|
771
|
+
const decoded = decodeJsonString(body);
|
|
772
|
+
elements.push({
|
|
773
|
+
start: index + 1,
|
|
774
|
+
end: end - 1,
|
|
775
|
+
value: decoded === null ? body : decoded,
|
|
776
|
+
});
|
|
777
|
+
index = end;
|
|
778
|
+
while (isWhitespace(text.charAt(index))) {
|
|
779
|
+
index++;
|
|
780
|
+
}
|
|
781
|
+
if (text.charAt(index) !== ",") {
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
index++;
|
|
785
|
+
}
|
|
786
|
+
const masked = redisMaskedIndexes(elements.map((element) => {
|
|
787
|
+
return element.value;
|
|
788
|
+
}));
|
|
789
|
+
for (let position = 0; position < elements.length; position++) {
|
|
790
|
+
const element = elements[position];
|
|
791
|
+
if (element.start >= cursor &&
|
|
792
|
+
masked.includes(position) &&
|
|
793
|
+
!isPlaceholder(element.value)) {
|
|
794
|
+
out += text.slice(cursor, element.start) + DATABASE_REDACTED_MARKER;
|
|
795
|
+
cursor = element.end;
|
|
796
|
+
count++;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
JSON_ARRAY_START_REGEX.lastIndex = Math.max(index, match.index + 1);
|
|
800
|
+
match = JSON_ARRAY_START_REGEX.exec(text);
|
|
801
|
+
}
|
|
802
|
+
out += text.slice(cursor);
|
|
803
|
+
return { text: out, redactionCount: count };
|
|
804
|
+
}
|
|
805
|
+
// One argument in plain text: stops at whitespace, a quote or a closing bracket.
|
|
806
|
+
const TEXT_ARGUMENT = "(?:[^\\s\"'\\]\\\\]|\\\\.)+";
|
|
807
|
+
/*
|
|
808
|
+
* PASSWORD 'x', ENCRYPTED PASSWORD "x", PASSWORD('x'), PASSWORD = 'x',
|
|
809
|
+
* IDENTIFIED BY 'x', IDENTIFIED WITH plugin BY|AS 'x'. Quoted values only,
|
|
810
|
+
* and only after a space, "=" or "(": "password authentication failed",
|
|
811
|
+
* PASSWORD EXPIRE and a JSON key "password": ... read as they are (the
|
|
812
|
+
* generic rules mask a credential key's value).
|
|
813
|
+
*/
|
|
814
|
+
const PASSWORD_PHRASE_REGEX = /\b((?:IDENTIFIED\s+(?:WITH\s+[A-Za-z0-9_$]+\s+)?(?:BY|AS)(?:\s+PASSWORD)?)|(?:(?:UN)?ENCRYPTED\s+)?PASSWORD)(\s*=\s*\(?\s*|\s*\(\s*|\s+)('(?:[^'\\\n]|\\.|'')*'?|"(?:[^"\\\n]|\\.|"")*"?)/gi;
|
|
815
|
+
/*
|
|
816
|
+
* AUTH [username] password where a Redis command starts (a line, a quoted
|
|
817
|
+
* field, a list item, after ": ", "= " or "| "): the rest of the field is
|
|
818
|
+
* masked. `cmd=auth` in CLIENT LIST names the command only and is left.
|
|
819
|
+
*/
|
|
820
|
+
const REDIS_AUTH_REGEX = /(^|[[("'\t)]|[:=|] )([ \t]*)(auth)([ \t]+)((?:[^"\\\]\n]|\\.)+)/gim;
|
|
821
|
+
// HELLO protover AUTH username password (AUTH comes right after protover).
|
|
822
|
+
const REDIS_HELLO_AUTH_REGEX = new RegExp(`(\\bhello[ \\t]+[^\\s\\]]+[ \\t]+auth[ \\t]+)(${TEXT_ARGUMENT})((?:[ \\t]+${TEXT_ARGUMENT})?)`, "gi");
|
|
823
|
+
/*
|
|
824
|
+
* MIGRATE host port key|"" db timeout [COPY] [REPLACE] AUTH password |
|
|
825
|
+
* AUTH2 username password: AUTH comes after five to seven words.
|
|
826
|
+
*/
|
|
827
|
+
const REDIS_MIGRATE_AUTH_REGEX = new RegExp(`(\\bmigrate(?:[ \\t]+[^\\s\\]]+){5,7}?[ \\t]+)(auth2?)([ \\t]+)(${TEXT_ARGUMENT})((?:[ \\t]+${TEXT_ARGUMENT})?)`, "gi");
|
|
828
|
+
/*
|
|
829
|
+
* requirepass / masterauth (primaryauth on Valkey) and the value after them, separated by spaces
|
|
830
|
+
* (CONFIG SET text) or on the next line (a redis-cli CONFIG GET reply:
|
|
831
|
+
* `1) "requirepass"` / `2) "value"`). JSON pairs and key: value lines are
|
|
832
|
+
* the generic rules' and maskJsonCredentialSettings' to mask.
|
|
833
|
+
*/
|
|
834
|
+
const REDIS_CREDENTIAL_SETTING_REGEX = new RegExp(`\\b(requirepass|masterauth|primaryauth)\\b((?:["']?[ \\t]*\\r?\\n[ \\t]*(?:\\d+\\)[ \\t]*)?|[ \\t]+)["']?)(${TEXT_ARGUMENT})`, "gi");
|
|
835
|
+
/*
|
|
836
|
+
* SHOW ENGINE INNODB STATUS prints the bytes of the rows a deadlocked or
|
|
837
|
+
* waiting transaction locked, one field per line: ` 0: len 4; hex
|
|
838
|
+
* 80000007; asc ;;`. They are table data (the asc rendering may hold
|
|
839
|
+
* any character, ";" included), so everything after "hex " to the end of
|
|
840
|
+
* the line is masked; the field's number and length stay.
|
|
841
|
+
*/
|
|
842
|
+
const INNODB_RECORD_DUMP_REGEX = /\blen [0-9]+; hex /;
|
|
843
|
+
function maskInnodbRecordDumps(text) {
|
|
844
|
+
let count = 0;
|
|
845
|
+
if (!text.includes("; hex ")) {
|
|
846
|
+
return { text, redactionCount: 0 };
|
|
847
|
+
}
|
|
848
|
+
const lines = text.split("\n").map((line) => {
|
|
849
|
+
const match = INNODB_RECORD_DUMP_REGEX.exec(line);
|
|
850
|
+
if (!match) {
|
|
851
|
+
return line;
|
|
852
|
+
}
|
|
853
|
+
const start = match.index + match[0].length;
|
|
854
|
+
const rest = line.slice(start);
|
|
855
|
+
if (!rest || rest.startsWith(DATABASE_REDACTED_MARKER)) {
|
|
856
|
+
return line;
|
|
857
|
+
}
|
|
858
|
+
count++;
|
|
859
|
+
const asc = rest.includes("; asc ")
|
|
860
|
+
? `; asc ${DATABASE_REDACTED_MARKER}`
|
|
861
|
+
: "";
|
|
862
|
+
const end = rest.endsWith(";;")
|
|
863
|
+
? ";;"
|
|
864
|
+
: rest.endsWith(";")
|
|
865
|
+
? ";"
|
|
866
|
+
: "";
|
|
867
|
+
return `${line.slice(0, start)}${DATABASE_REDACTED_MARKER}${asc}${end}`;
|
|
868
|
+
});
|
|
869
|
+
return { text: lines.join("\n"), redactionCount: count };
|
|
870
|
+
}
|
|
871
|
+
function isMaskedWord(value) {
|
|
872
|
+
const bare = value.replace(/^['"]|['"]$/g, "");
|
|
873
|
+
return isPlaceholder(bare) || bare.startsWith("[redacted");
|
|
874
|
+
}
|
|
875
|
+
function maskSecretPhrases(text) {
|
|
876
|
+
let count = 0;
|
|
877
|
+
const maskWord = (word) => {
|
|
878
|
+
if (!word || isMaskedWord(word)) {
|
|
879
|
+
return word;
|
|
880
|
+
}
|
|
881
|
+
count++;
|
|
882
|
+
return DATABASE_REDACTED_MARKER;
|
|
883
|
+
};
|
|
884
|
+
// " user pass" -> " [redacted] [redacted]": each word masked, the spacing kept.
|
|
885
|
+
const maskWords = (words) => {
|
|
886
|
+
return words.replace(/\S+/g, (word) => {
|
|
887
|
+
return maskWord(word);
|
|
888
|
+
});
|
|
889
|
+
};
|
|
890
|
+
const masked = text
|
|
891
|
+
.replace(PASSWORD_PHRASE_REGEX, (whole, phrase, separator, value) => {
|
|
892
|
+
const quote = value.charAt(0);
|
|
893
|
+
let body = value.slice(1);
|
|
894
|
+
if (body.endsWith(quote)) {
|
|
895
|
+
body = body.slice(0, -1);
|
|
896
|
+
}
|
|
897
|
+
if (!body || isMaskedWord(body)) {
|
|
898
|
+
return whole;
|
|
899
|
+
}
|
|
900
|
+
count++;
|
|
901
|
+
return `${phrase}${separator}${quote}${DATABASE_REDACTED_MARKER}${quote}`;
|
|
902
|
+
})
|
|
903
|
+
.replace(REDIS_AUTH_REGEX, (whole, lead, space, auth, gap, rest) => {
|
|
904
|
+
const trailingMatch = TRAILING_WHITESPACE_REGEX.exec(rest);
|
|
905
|
+
const trailing = trailingMatch ? trailingMatch[0] : "";
|
|
906
|
+
const words = rest.slice(0, rest.length - trailing.length);
|
|
907
|
+
if (!words || isMaskedWord(words)) {
|
|
908
|
+
return whole;
|
|
909
|
+
}
|
|
910
|
+
count++;
|
|
911
|
+
return `${lead}${space}${auth}${gap}${DATABASE_REDACTED_MARKER}${trailing}`;
|
|
912
|
+
})
|
|
913
|
+
.replace(REDIS_HELLO_AUTH_REGEX, (_whole, lead, first, second) => {
|
|
914
|
+
return `${lead}${maskWord(first)}${maskWords(second || "")}`;
|
|
915
|
+
})
|
|
916
|
+
.replace(REDIS_MIGRATE_AUTH_REGEX, (_whole, lead, auth, gap, first, second) => {
|
|
917
|
+
const rest = auth.toLowerCase() === "auth2"
|
|
918
|
+
? maskWords(second || "")
|
|
919
|
+
: second || "";
|
|
920
|
+
return `${lead}${auth}${gap}${maskWord(first)}${rest}`;
|
|
921
|
+
})
|
|
922
|
+
.replace(REDIS_CREDENTIAL_SETTING_REGEX, (_whole, name, separator, value) => {
|
|
923
|
+
return `${name}${separator}${maskWord(value)}`;
|
|
924
|
+
});
|
|
925
|
+
return { text: masked, redactionCount: count };
|
|
926
|
+
}
|
|
927
|
+
// Is the keyword at `index` at the start of a field of plain-text output?
|
|
928
|
+
function isFieldStart(line, index) {
|
|
929
|
+
let before = index - 1;
|
|
930
|
+
let spaces = 0;
|
|
931
|
+
let sawTab = false;
|
|
932
|
+
while (before >= 0 &&
|
|
933
|
+
(line.charAt(before) === " " || line.charAt(before) === "\t")) {
|
|
934
|
+
if (line.charAt(before) === "\t") {
|
|
935
|
+
sawTab = true;
|
|
936
|
+
}
|
|
937
|
+
spaces++;
|
|
938
|
+
before--;
|
|
939
|
+
}
|
|
940
|
+
if (before < 0 || sawTab || spaces >= 2) {
|
|
941
|
+
return true;
|
|
942
|
+
}
|
|
943
|
+
return ":=|(>;,".includes(line.charAt(before));
|
|
944
|
+
}
|
|
945
|
+
// Could the single quote at `index` open a literal (not an apostrophe inside a word)?
|
|
946
|
+
function isLiteralOpening(line, index) {
|
|
947
|
+
const previous = line.charAt(index - 1);
|
|
948
|
+
if (!isWordChar(previous)) {
|
|
949
|
+
return true;
|
|
950
|
+
}
|
|
951
|
+
return STRING_PREFIX_REGEX.test(line.slice(Math.max(0, index - 40), index));
|
|
952
|
+
}
|
|
953
|
+
// One line of plain-text output: statements normalized, single-quoted literals masked.
|
|
954
|
+
function maskTextLine(line) {
|
|
955
|
+
let out = "";
|
|
956
|
+
let count = 0;
|
|
957
|
+
let index = 0;
|
|
958
|
+
// Right after a statement a column stop cut short: its next clause continues it.
|
|
959
|
+
let afterStatement = false;
|
|
960
|
+
// Once a double quote does not close, no later one on the line can (see scanJsonString).
|
|
961
|
+
let quotesArePlain = false;
|
|
962
|
+
const firstWord = line.search(/\S/);
|
|
963
|
+
while (index < line.length) {
|
|
964
|
+
const ch = line.charAt(index);
|
|
965
|
+
if (ch === '"' && !quotesArePlain) {
|
|
966
|
+
const scan = scanJsonString(line, index);
|
|
967
|
+
// A JSON string (normalized already when it held a statement) or a name.
|
|
968
|
+
if (scan.closed) {
|
|
969
|
+
out += line.slice(index, scan.end);
|
|
970
|
+
index = scan.end;
|
|
971
|
+
afterStatement = false;
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
quotesArePlain = true;
|
|
975
|
+
}
|
|
976
|
+
if (ch === "'" && isLiteralOpening(line, index)) {
|
|
977
|
+
const end = scanSingleQuoted(line, index);
|
|
978
|
+
const closed = isClosed(line, index, end, "'");
|
|
979
|
+
if (closed && isPlaceholder(line.slice(index + 1, end - 1))) {
|
|
980
|
+
out += line.slice(index, end);
|
|
981
|
+
}
|
|
982
|
+
else {
|
|
983
|
+
out += `'${DATABASE_LITERAL_PLACEHOLDER}'`;
|
|
984
|
+
count++;
|
|
985
|
+
}
|
|
986
|
+
index = end;
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
if (isWordStart(ch) && !isWordChar(line.charAt(index - 1))) {
|
|
990
|
+
const keyword = keywordAt(line, index);
|
|
991
|
+
const startsStatement = (STATEMENT_KEYWORDS.has(keyword) && isFieldStart(line, index)) ||
|
|
992
|
+
(CONTINUATION_KEYWORDS.has(keyword) &&
|
|
993
|
+
(index === firstWord ||
|
|
994
|
+
(afterStatement && isFieldStart(line, index))));
|
|
995
|
+
if (startsStatement) {
|
|
996
|
+
const lexed = lexStatement(line, index, true);
|
|
997
|
+
out += lexed.out;
|
|
998
|
+
count += lexed.count;
|
|
999
|
+
index = Math.max(lexed.end, index + 1);
|
|
1000
|
+
afterStatement = true;
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
let end = index + 1;
|
|
1004
|
+
while (end < line.length && isWordChar(line.charAt(end))) {
|
|
1005
|
+
end++;
|
|
1006
|
+
}
|
|
1007
|
+
out += line.slice(index, end);
|
|
1008
|
+
index = end;
|
|
1009
|
+
afterStatement = false;
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
out += ch;
|
|
1013
|
+
index++;
|
|
1014
|
+
}
|
|
1015
|
+
return { text: out, redactionCount: count };
|
|
1016
|
+
}
|
|
1017
|
+
function maskTextLines(text) {
|
|
1018
|
+
let count = 0;
|
|
1019
|
+
const lines = text.split("\n").map((line) => {
|
|
1020
|
+
const masked = maskTextLine(line);
|
|
1021
|
+
count += masked.redactionCount;
|
|
1022
|
+
return masked.text;
|
|
1023
|
+
});
|
|
1024
|
+
return { text: lines.join("\n"), redactionCount: count };
|
|
1025
|
+
}
|
|
1026
|
+
// The passes, in order: credentials first, then JSON, then plain text.
|
|
1027
|
+
const REDACTION_PASSES = [
|
|
1028
|
+
maskSecretPhrases,
|
|
1029
|
+
maskInnodbRecordDumps,
|
|
1030
|
+
maskJsonCredentialSettings,
|
|
1031
|
+
maskRedisCommandArrays,
|
|
1032
|
+
maskJsonStatementStrings,
|
|
1033
|
+
maskTextLines,
|
|
1034
|
+
];
|
|
1035
|
+
/*
|
|
1036
|
+
* Normalize every statement in a `db` command's output and mask the
|
|
1037
|
+
* credentials and row data database output carries. The "db" hook of
|
|
1038
|
+
* ResourceOutputRedactor (a ResourceOutputRedactionHook); the generic rules
|
|
1039
|
+
* run after it.
|
|
1040
|
+
*/
|
|
1041
|
+
export function redactDatabaseOutput(data) {
|
|
1042
|
+
if (!data || typeof data.text !== "string" || !data.text) {
|
|
1043
|
+
return { text: "", redactionCount: 0 };
|
|
1044
|
+
}
|
|
1045
|
+
try {
|
|
1046
|
+
let text = data.text;
|
|
1047
|
+
let count = 0;
|
|
1048
|
+
for (const pass of REDACTION_PASSES) {
|
|
1049
|
+
const result = pass(text);
|
|
1050
|
+
text = result.text;
|
|
1051
|
+
count += result.redactionCount;
|
|
1052
|
+
}
|
|
1053
|
+
return { text, redactionCount: count };
|
|
1054
|
+
}
|
|
1055
|
+
catch (_a) {
|
|
1056
|
+
// Never pass on output that could not be normalized.
|
|
1057
|
+
return { text: DATABASE_UNREDACTABLE_OUTPUT, redactionCount: 1 };
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
//# sourceMappingURL=DatabaseQueryRedactor.js.map
|