@oneuptime/common 12.0.12 → 12.0.13

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 (234) hide show
  1. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
  2. package/Models/DatabaseModels/NetworkDevice.ts +64 -0
  3. package/Models/DatabaseModels/StatusPage.ts +59 -0
  4. package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
  5. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
  6. package/Server/API/AIChatAPI.ts +55 -56
  7. package/Server/API/CommonAPI.ts +89 -0
  8. package/Server/API/MicrosoftTeamsAPI.ts +20 -9
  9. package/Server/API/SlackAPI.ts +13 -10
  10. package/Server/API/StatusPageAPI.ts +2197 -2128
  11. package/Server/API/TelemetryAPI.ts +72 -3
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  15. package/Server/Middleware/UserAuthorization.ts +11 -2
  16. package/Server/Services/ApiKeyPermissionService.ts +116 -2
  17. package/Server/Services/DatabaseService.ts +17 -6
  18. package/Server/Services/LogAggregationService.ts +34 -0
  19. package/Server/Services/LogService.ts +21 -0
  20. package/Server/Services/NetworkSiteService.ts +12 -0
  21. package/Server/Services/SpanService.ts +21 -0
  22. package/Server/Services/TraceAggregationService.ts +15 -0
  23. package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
  24. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  25. package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
  26. package/Server/Utils/APIKey/AccessPermission.ts +16 -40
  27. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
  28. package/Server/Utils/LogRedaction.ts +576 -0
  29. package/Server/Utils/Logger.ts +123 -46
  30. package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
  31. package/Server/Utils/Monitor/MonitorResource.ts +29 -9
  32. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
  33. package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
  34. package/Server/Utils/StartServer.ts +30 -9
  35. package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
  36. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
  37. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
  38. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
  39. package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
  40. package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
  41. package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
  42. package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
  43. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
  44. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
  45. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
  46. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
  47. package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
  48. package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
  49. package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
  50. package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
  51. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
  52. package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
  53. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
  54. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
  55. package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
  56. package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
  57. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
  58. package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
  59. package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
  60. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
  61. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
  62. package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
  63. package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
  64. package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
  65. package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
  66. package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
  67. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
  68. package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
  69. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
  70. package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
  71. package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
  72. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
  73. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
  74. package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
  75. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
  76. package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
  77. package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
  78. package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
  79. package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
  80. package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
  81. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
  82. package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
  83. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
  84. package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
  85. package/Tests/UI/Components/MasterPage.test.tsx +36 -1
  86. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
  87. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
  88. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
  89. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
  90. package/Types/API/Hostname.ts +79 -0
  91. package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
  92. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
  93. package/Types/Database/DatabaseProperty.ts +38 -0
  94. package/Types/Database/TableColumn.ts +34 -7
  95. package/Types/Events/Recurring.ts +16 -1
  96. package/Types/Monitor/MonitorStep.ts +13 -1
  97. package/Types/Monitor/MonitorType.ts +345 -40
  98. package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
  99. package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
  100. package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
  101. package/UI/Components/CardSelect/CardSelect.tsx +714 -105
  102. package/UI/Components/EmptyState/EmptyState.tsx +8 -1
  103. package/UI/Components/Footer/Footer.tsx +32 -6
  104. package/UI/Components/Forms/Fields/FormField.tsx +3 -0
  105. package/UI/Components/Forms/Types/Field.ts +8 -0
  106. package/UI/Components/Loader/PageLoader.tsx +29 -8
  107. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
  108. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
  109. package/UI/Components/MasterPage/MasterPage.tsx +24 -11
  110. package/Utils/Monitor/MonitorMetricType.ts +16 -1
  111. package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
  112. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
  113. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
  114. package/Utils/Telemetry/CrossSignalScope.ts +75 -4
  115. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
  116. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  117. package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
  118. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  119. package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
  120. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  121. package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
  122. package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
  123. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  124. package/build/dist/Server/API/AIChatAPI.js +48 -35
  125. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  126. package/build/dist/Server/API/CommonAPI.js +59 -0
  127. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  128. package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
  129. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  130. package/build/dist/Server/API/SlackAPI.js +13 -10
  131. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  132. package/build/dist/Server/API/StatusPageAPI.js +745 -673
  133. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  134. package/build/dist/Server/API/TelemetryAPI.js +34 -3
  135. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  136. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
  137. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
  138. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
  139. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
  140. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  141. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  142. package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
  143. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  144. package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
  145. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  146. package/build/dist/Server/Services/DatabaseService.js +15 -6
  147. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  148. package/build/dist/Server/Services/LogAggregationService.js +2 -0
  149. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  150. package/build/dist/Server/Services/LogService.js +16 -0
  151. package/build/dist/Server/Services/LogService.js.map +1 -1
  152. package/build/dist/Server/Services/NetworkSiteService.js +12 -0
  153. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  154. package/build/dist/Server/Services/SpanService.js +16 -0
  155. package/build/dist/Server/Services/SpanService.js.map +1 -1
  156. package/build/dist/Server/Services/TraceAggregationService.js +2 -0
  157. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  158. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
  159. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  160. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  161. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  162. package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
  163. package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
  164. package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
  165. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  166. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
  167. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  168. package/build/dist/Server/Utils/LogRedaction.js +449 -0
  169. package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
  170. package/build/dist/Server/Utils/Logger.js +98 -45
  171. package/build/dist/Server/Utils/Logger.js.map +1 -1
  172. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
  173. package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
  174. package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
  175. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  176. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
  177. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  178. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
  179. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
  180. package/build/dist/Server/Utils/StartServer.js +10 -7
  181. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  182. package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
  183. package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
  184. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
  185. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
  186. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
  187. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  188. package/build/dist/Types/API/Hostname.js +63 -0
  189. package/build/dist/Types/API/Hostname.js.map +1 -1
  190. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
  191. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  192. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
  193. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  194. package/build/dist/Types/Database/DatabaseProperty.js +38 -0
  195. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  196. package/build/dist/Types/Database/TableColumn.js +24 -6
  197. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  198. package/build/dist/Types/Events/Recurring.js +13 -1
  199. package/build/dist/Types/Events/Recurring.js.map +1 -1
  200. package/build/dist/Types/Monitor/MonitorStep.js +9 -1
  201. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  202. package/build/dist/Types/Monitor/MonitorType.js +330 -31
  203. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  204. package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
  205. package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
  206. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
  207. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
  208. package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
  209. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  210. package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
  211. package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
  212. package/build/dist/UI/Components/Footer/Footer.js +7 -4
  213. package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
  214. package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
  215. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  216. package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
  217. package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
  218. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
  219. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  220. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
  221. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  222. package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
  223. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  224. package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
  225. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  226. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
  227. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  228. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
  229. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
  230. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
  231. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
  232. package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
  233. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  234. package/package.json +1 -1
@@ -0,0 +1,449 @@
1
+ /*
2
+ * Centralized, deny-by-default redaction for everything that reaches a log
3
+ * sink.
4
+ *
5
+ * Every log record this process emits is written to three places: process
6
+ * stdout/stderr, the in-memory recent-log ring buffer that the master-admin
7
+ * support bundle surfaces, and the configured OpenTelemetry log exporter. A
8
+ * credential that reaches a log statement therefore lands in three retention
9
+ * systems at once, and DEBUG is a normal troubleshooting level operators turn
10
+ * on in production.
11
+ *
12
+ * So call sites are not trusted to be careful. Logger runs every message body
13
+ * and every attribute through here first, at every level, and the rules below
14
+ * are the single place that decides what a secret looks like. Removing a
15
+ * careless `logger.debug(requestBody)` is still the right fix at the call
16
+ * site -- this is the net underneath it.
17
+ *
18
+ * Two matching strategies, both applied:
19
+ *
20
+ * 1. Structural. Objects are walked recursively and a value is replaced
21
+ * whenever its KEY names a credential (`client_secret`, `access_token`,
22
+ * `password`, `Authorization`, ...). This is the reliable one: it does
23
+ * not care what the value looks like.
24
+ *
25
+ * 2. Textual. Strings are swept for credentials that were already flattened
26
+ * into text -- `JSON.stringify(req.body)` in a template literal, an OAuth
27
+ * callback URL with `?code=`, an `Authorization: Bearer` header dump -- and
28
+ * for values that are self-identifying regardless of key (JWTs, Slack
29
+ * `xoxb-` tokens, PEM private key blocks).
30
+ *
31
+ * Over-redaction is the intended failure mode. Losing a token count from a
32
+ * debug line costs a little context; leaking a bot token costs the workspace.
33
+ */
34
+ export const REDACTED = "[REDACTED]";
35
+ // Deep enough for any real log payload, shallow enough to bound the walk.
36
+ const MAX_DEPTH = 12;
37
+ /*
38
+ * A key is sensitive when its normalized form (lowercased, separators
39
+ * stripped, so `client_secret`, `clientSecret` and `CLIENT-SECRET` all collapse
40
+ * to `clientsecret`) CONTAINS one of these fragments. Substring matching is
41
+ * deliberate: it catches `slackBotAccessToken`, `x-api-key`,
42
+ * `refresh_token_expires_in` and every other spelling nobody thought to list.
43
+ */
44
+ const SENSITIVE_KEY_FRAGMENTS = [
45
+ "password",
46
+ "passwd",
47
+ "passphrase",
48
+ "secret",
49
+ "token",
50
+ "credential",
51
+ "authorization",
52
+ "cookie",
53
+ "apikey",
54
+ "privatekey",
55
+ "accesskey",
56
+ "secretkey",
57
+ "signingkey",
58
+ "encryptionkey",
59
+ "assertion",
60
+ "attestation",
61
+ "signature",
62
+ "salt",
63
+ "otp",
64
+ "jwt",
65
+ "bearer",
66
+ "sessionid",
67
+ "authcode",
68
+ "authorizationcode",
69
+ "verificationcode",
70
+ "backupcode",
71
+ "recoverycode",
72
+ "onetimecode",
73
+ "logincode",
74
+ "resetcode",
75
+ ];
76
+ /*
77
+ * Keys that contain a sensitive fragment but never carry the secret itself.
78
+ * These are worth keeping: LLM token accounting and expiry timestamps are
79
+ * exactly the sort of thing someone turns DEBUG on to look at.
80
+ */
81
+ const NON_SENSITIVE_KEYS = new Set([
82
+ "tokencount",
83
+ "tokencounts",
84
+ "tokensused",
85
+ "totaltokens",
86
+ "inputtokens",
87
+ "outputtokens",
88
+ "prompttokens",
89
+ "completiontokens",
90
+ "cachedtokens",
91
+ "reasoningtokens",
92
+ "maxtokens",
93
+ "tokenlimit",
94
+ "tokenusage",
95
+ "tokentype",
96
+ "hastoken",
97
+ "hasaccesstoken",
98
+ "hasrefreshtoken",
99
+ "tokenexpiresat",
100
+ "tokenexpiry",
101
+ "accesstokenexpiresat",
102
+ "refreshtokenexpiresat",
103
+ "expirestokenat",
104
+ "isauthorized",
105
+ "authorizationurl",
106
+ "authorizationendpoint",
107
+ ]);
108
+ /*
109
+ * Keys whose sensitivity depends on the value. `code` is both the OAuth
110
+ * authorization code / TOTP code we must never log AND the `ECONNREFUSED` on
111
+ * every network error, so it is decided by looksLikeSecretCode below.
112
+ */
113
+ const VALUE_DEPENDENT_KEYS = new Set(["code", "codes"]);
114
+ /*
115
+ * Keys that are always secret but carry no fragment above -- WebAuthn
116
+ * assertion material, mostly.
117
+ */
118
+ const SENSITIVE_EXACT_KEYS = new Set([
119
+ "pin",
120
+ "totp",
121
+ "mfa",
122
+ "rawid",
123
+ "clientdatajson",
124
+ "authenticatordata",
125
+ "userhandle",
126
+ "challenge",
127
+ "twofactorsecret",
128
+ ]);
129
+ export const normalizeLogKey = (key) => {
130
+ return key.toLowerCase().replace(/[^a-z0-9]/g, "");
131
+ };
132
+ /*
133
+ * Error codes (`ECONNREFUSED`, `ERR_BAD_REQUEST`), exception codes
134
+ * (`BadDataException`) and short status-ish strings are identifiers, never
135
+ * credentials. Anything else under a `code` key -- digits (a TOTP), or a long
136
+ * opaque string with separators (an OAuth authorization code) -- is treated as
137
+ * a secret.
138
+ */
139
+ const IDENTIFIER_CODE_REGEX = /^[A-Za-z][A-Za-z0-9]*$/;
140
+ const looksLikeSecretCode = (value) => {
141
+ if (typeof value === "number") {
142
+ // HTTP status / exit codes are small; a 6-digit TOTP is not.
143
+ return Math.abs(value) >= 1000;
144
+ }
145
+ if (typeof value !== "string") {
146
+ return false;
147
+ }
148
+ if (value.length === 0) {
149
+ return false;
150
+ }
151
+ return !(IDENTIFIER_CODE_REGEX.test(value) && value.length < 32);
152
+ };
153
+ export const isSensitiveLogKey = (key, value) => {
154
+ const normalized = normalizeLogKey(key);
155
+ if (!normalized) {
156
+ return false;
157
+ }
158
+ if (NON_SENSITIVE_KEYS.has(normalized)) {
159
+ return false;
160
+ }
161
+ if (SENSITIVE_EXACT_KEYS.has(normalized)) {
162
+ return true;
163
+ }
164
+ if (VALUE_DEPENDENT_KEYS.has(normalized)) {
165
+ return looksLikeSecretCode(value);
166
+ }
167
+ return SENSITIVE_KEY_FRAGMENTS.some((fragment) => {
168
+ return normalized.includes(fragment);
169
+ });
170
+ };
171
+ /*
172
+ * ---------------------------------------------------------------------------
173
+ * String rules
174
+ * ---------------------------------------------------------------------------
175
+ */
176
+ /*
177
+ * Credential words as they appear INSIDE text. Kept separate from the key
178
+ * fragment list because a couple of the structural fragments (`salt`, `otp`)
179
+ * are too short to match safely against free-form prose.
180
+ */
181
+ const TEXT_SECRET_WORDS = "password|passwd|passphrase|secret|token|credential|credentials|authorization|cookie|api[_-]?key|private[_-]?key|access[_-]?key|assertion|attestation|signature|raw[_-]?id|client[_-]?data[_-]?json|authenticator[_-]?data|user[_-]?handle|two[_-]?factor[_-]?secret";
182
+ // Bounded, so a long non-matching run cannot blow up on backtracking.
183
+ const KEY_PREFIX = "[A-Za-z0-9_.-]{0,40}";
184
+ const KEY_SUFFIX = "[A-Za-z0-9_.-]{0,40}";
185
+ const STRING_REDACTION_RULES = [
186
+ {
187
+ /*
188
+ * Runs first: a bare `Bearer <token>` with no key in front of it would
189
+ * otherwise survive every rule below.
190
+ *
191
+ * The lookahead requires the value to carry a digit or a separator, so the
192
+ * scheme words keep working as English -- "Basic authentication failed" and
193
+ * "Token exchange completed" are log lines, not credentials.
194
+ */
195
+ name: "authorization-scheme",
196
+ regex: /\b(Bearer|Basic|Token)\s+(?=[A-Za-z0-9._~+/=-]*[0-9._~+/=-])[A-Za-z0-9._~+/=-]{8,}/gi,
197
+ replacement: `$1 ${REDACTED}`,
198
+ },
199
+ {
200
+ /*
201
+ * JSON string members: {"client_secret":"abc"} -- the shape produced by
202
+ * JSON.stringify(req.body) inside a log message. The value pattern allows
203
+ * escaped quotes so an escaped-quote-bearing password cannot end the match
204
+ * early and leave its tail in the clear.
205
+ */
206
+ name: "json-string-member",
207
+ regex: new RegExp(`"(${KEY_PREFIX}(?:${TEXT_SECRET_WORDS})${KEY_SUFFIX})"(\\s*:\\s*)"(?:[^"\\\\]|\\\\.)*"`, "gi"),
208
+ replacement: `"$1"$2"${REDACTED}"`,
209
+ },
210
+ {
211
+ // JSON numeric members: {"password":123456}
212
+ name: "json-number-member",
213
+ regex: new RegExp(`"(${KEY_PREFIX}(?:${TEXT_SECRET_WORDS})${KEY_SUFFIX})"(\\s*:\\s*)-?\\d+(?:\\.\\d+)?`, "gi"),
214
+ replacement: `"$1"$2"${REDACTED}"`,
215
+ },
216
+ {
217
+ /*
218
+ * Unquoted key/value text: form bodies (`client_secret=abc&...`), query
219
+ * strings, header dumps (`Authorization: Bearer abc`) and prose
220
+ * (`password: hunter2`).
221
+ *
222
+ * The value alternation tries `Bearer <token>` first, so a header dump is
223
+ * consumed whole -- stopping at the scheme word would leave the token
224
+ * itself sitting in the clear right after the placeholder.
225
+ */
226
+ name: "key-value-text",
227
+ regex: new RegExp(`\\b(${KEY_PREFIX}(?:${TEXT_SECRET_WORDS})${KEY_SUFFIX})(\\s*[=:]\\s*)(?:"[^"]*"|'[^']*'|(?:Bearer|Basic|Token)\\s+[^\\s,;&)\\]}"']+|[^\\s,;&)\\]}"']+)`, "gi"),
228
+ replacement: `$1$2${REDACTED}`,
229
+ },
230
+ {
231
+ /*
232
+ * OAuth authorization codes and OTPs as query/form parameters. Restricted
233
+ * to the `code=` form so ordinary prose ("error code: ECONNREFUSED") keeps
234
+ * its meaning -- structural redaction covers the JSON case.
235
+ */
236
+ name: "code-parameter",
237
+ regex: /\b(code|pin|otp|totp)(=)[^\s&#"'<>]+/gi,
238
+ replacement: `$1$2${REDACTED}`,
239
+ },
240
+ {
241
+ // {"code":"..."} in flattened JSON.
242
+ name: "json-code-member",
243
+ regex: /"(code|pin|otp|totp)"(\s*:\s*)"(?:[^"\\]|\\.)*"/gi,
244
+ replacement: `"$1"$2"${REDACTED}"`,
245
+ },
246
+ {
247
+ name: "jwt",
248
+ regex: /\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}/g,
249
+ replacement: REDACTED,
250
+ },
251
+ {
252
+ name: "slack-token",
253
+ regex: /\bxox[abeprs]-[A-Za-z0-9-]{8,}/gi,
254
+ replacement: REDACTED,
255
+ },
256
+ {
257
+ name: "slack-app-token",
258
+ regex: /\bxapp-[A-Za-z0-9-]{8,}/gi,
259
+ replacement: REDACTED,
260
+ },
261
+ {
262
+ name: "github-token",
263
+ regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b|\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
264
+ replacement: REDACTED,
265
+ },
266
+ {
267
+ name: "aws-access-key-id",
268
+ regex: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASCA)[0-9A-Z]{16}\b/g,
269
+ replacement: REDACTED,
270
+ },
271
+ {
272
+ name: "google-api-key",
273
+ regex: /\bAIza[0-9A-Za-z_-]{35}\b/g,
274
+ replacement: REDACTED,
275
+ },
276
+ {
277
+ name: "private-key-block",
278
+ regex: /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9]+ )*PRIVATE KEY-----/g,
279
+ replacement: REDACTED,
280
+ },
281
+ {
282
+ // Credentials embedded in a URL: postgres://user:password@host/db
283
+ name: "url-basic-auth",
284
+ regex: /\b([a-z][a-z0-9+.-]*:\/\/)([^\s/:@]+):[^\s/@]+@/gi,
285
+ replacement: `$1$2:${REDACTED}@`,
286
+ },
287
+ ];
288
+ /*
289
+ * Cheap pre-filter. Almost every log line contains none of these, and skipping
290
+ * fifteen regexes for those keeps redaction off the cost of ordinary logging.
291
+ * It must stay a superset of every rule above -- a word missing here is a
292
+ * credential that never gets redacted.
293
+ */
294
+ const SENSITIVE_TEXT_HINT_REGEX = new RegExp([
295
+ TEXT_SECRET_WORDS,
296
+ "code=",
297
+ "pin=",
298
+ "otp=",
299
+ "totp=",
300
+ '"code"',
301
+ '"pin"',
302
+ '"otp"',
303
+ '"totp"',
304
+ "bearer\\s",
305
+ "basic\\s",
306
+ "eyJ",
307
+ "xox",
308
+ "xapp-",
309
+ "gh[opusr]_",
310
+ "github_pat_",
311
+ "AKIA|ASIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASCA",
312
+ "AIza",
313
+ "-----BEGIN",
314
+ "://",
315
+ ].join("|"), "i");
316
+ export const redactLogString = (value) => {
317
+ if (!value) {
318
+ return value;
319
+ }
320
+ if (!SENSITIVE_TEXT_HINT_REGEX.test(value)) {
321
+ return value;
322
+ }
323
+ let redacted = value;
324
+ for (const rule of STRING_REDACTION_RULES) {
325
+ /*
326
+ * The rules carry the /g flag and are module-level, so lastIndex has to be
327
+ * reset -- String.replace does that itself, but .test() on the hint regex
328
+ * above and any future .test() here would not.
329
+ */
330
+ rule.regex.lastIndex = 0;
331
+ redacted = redacted.replace(rule.regex, rule.replacement);
332
+ }
333
+ return redacted;
334
+ };
335
+ const redactValue = (value, depth, seen) => {
336
+ if (value === null || value === undefined) {
337
+ return value;
338
+ }
339
+ if (typeof value === "string") {
340
+ return redactLogString(value);
341
+ }
342
+ if (typeof value === "number" ||
343
+ typeof value === "boolean" ||
344
+ typeof value === "bigint") {
345
+ return value;
346
+ }
347
+ if (typeof value === "function") {
348
+ return "[Function]";
349
+ }
350
+ if (typeof value === "symbol") {
351
+ return value.toString();
352
+ }
353
+ if (value instanceof Date) {
354
+ return value;
355
+ }
356
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
357
+ // Buffers hold key material as often as they hold anything readable.
358
+ return `[Buffer ${value.length} bytes]`;
359
+ }
360
+ if (depth >= MAX_DEPTH) {
361
+ return "[Truncated]";
362
+ }
363
+ if (seen.has(value)) {
364
+ return "[Circular]";
365
+ }
366
+ seen.add(value);
367
+ try {
368
+ if (value instanceof Error) {
369
+ const redactedError = {
370
+ name: value.name,
371
+ message: redactLogString(value.message),
372
+ };
373
+ if (value.stack) {
374
+ redactedError["stack"] = redactLogString(value.stack);
375
+ }
376
+ for (const key of Object.keys(value)) {
377
+ redactedError[key] = isSensitiveLogKey(key, value[key])
378
+ ? REDACTED
379
+ : redactValue(value[key], depth + 1, seen);
380
+ }
381
+ return redactedError;
382
+ }
383
+ if (Array.isArray(value)) {
384
+ return value.map((item) => {
385
+ return redactValue(item, depth + 1, seen);
386
+ });
387
+ }
388
+ if (value instanceof Map) {
389
+ const fromMap = {};
390
+ for (const [mapKey, mapValue] of value.entries()) {
391
+ const keyAsString = String(mapKey);
392
+ fromMap[keyAsString] = isSensitiveLogKey(keyAsString, mapValue)
393
+ ? REDACTED
394
+ : redactValue(mapValue, depth + 1, seen);
395
+ }
396
+ return fromMap;
397
+ }
398
+ if (value instanceof Set) {
399
+ return Array.from(value).map((item) => {
400
+ return redactValue(item, depth + 1, seen);
401
+ });
402
+ }
403
+ /*
404
+ * Models, ObjectIDs and the other Common types serialize through toJSON.
405
+ * Honouring it keeps redacted output shaped exactly like what
406
+ * JSON.stringify would have produced without this pass.
407
+ */
408
+ const maybeToJSON = value["toJSON"];
409
+ if (typeof maybeToJSON === "function") {
410
+ try {
411
+ const json = maybeToJSON.call(value);
412
+ if (json !== value) {
413
+ return redactValue(json, depth, seen);
414
+ }
415
+ }
416
+ catch (_a) {
417
+ // Fall through to the plain-object walk below.
418
+ }
419
+ }
420
+ const redactedObject = {};
421
+ for (const key of Object.keys(value)) {
422
+ const propertyValue = value[key];
423
+ redactedObject[key] = isSensitiveLogKey(key, propertyValue)
424
+ ? REDACTED
425
+ : redactValue(propertyValue, depth + 1, seen);
426
+ }
427
+ return redactedObject;
428
+ }
429
+ finally {
430
+ /*
431
+ * Only the path currently being walked is "seen", so a value referenced
432
+ * twice in a tree is redacted twice instead of being reported as circular.
433
+ */
434
+ seen.delete(value);
435
+ }
436
+ };
437
+ export const redactLogValue = (value) => {
438
+ try {
439
+ return redactValue(value, 0, new Set());
440
+ }
441
+ catch (_a) {
442
+ /*
443
+ * A getter that throws, an exotic proxy... nothing here is worth crashing
444
+ * a log call for, but the body cannot be passed through unredacted either.
445
+ */
446
+ return REDACTED;
447
+ }
448
+ };
449
+ //# sourceMappingURL=LogRedaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogRedaction.js","sourceRoot":"","sources":["../../../../Server/Utils/LogRedaction.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import TelemetryContext from "./Telemetry/TelemetryContext";
4
4
  import { SeverityNumber } from "@opentelemetry/api-logs";
5
5
  import Exception from "../../Types/Exception/Exception";
6
6
  import ConfigLogLevel from "../Types/ConfigLogLevel";
7
+ import { REDACTED, isSensitiveLogKey, redactLogString, redactLogValue, } from "./LogRedaction";
7
8
  export function getLogAttributesFromRequest(req) {
8
9
  var _a;
9
10
  if (!req) {
@@ -27,9 +28,10 @@ export function getLogAttributesFromRequest(req) {
27
28
  }
28
29
  }
29
30
  class logger {
31
+ // `body` has already been through redactBody -- see the level methods.
30
32
  static record(level, body) {
31
33
  try {
32
- const message = this.serializeLogBody(body);
34
+ const message = this.serializeRedactedBody(body);
33
35
  this.recentLogs.push({
34
36
  time: new Date().toISOString(),
35
37
  level: level,
@@ -64,14 +66,66 @@ class logger {
64
66
  }
65
67
  return LogLevel;
66
68
  }
67
- static serializeLogBody(body) {
68
- if (typeof body === "string") {
69
- return body;
69
+ /*
70
+ * Turns a body into a redacted copy, once, before it is handed to any sink.
71
+ * Every level method calls this and then passes the RESULT to console, to
72
+ * the recent-log buffer and to telemetry, so a log record is redacted a
73
+ * single time no matter how many places it ends up in.
74
+ *
75
+ * Objects come back as plain redacted copies, so console keeps printing them
76
+ * as objects rather than as a JSON string.
77
+ *
78
+ * Errors are special-cased: when nothing in the message or the stack needed
79
+ * redacting -- the overwhelmingly common case -- the original Error is
80
+ * returned untouched, so console still renders a real stack trace.
81
+ */
82
+ static redactBody(body) {
83
+ try {
84
+ if (typeof body === "string") {
85
+ return redactLogString(body);
86
+ }
87
+ if (body instanceof Exception || body instanceof Error) {
88
+ const originalStack = body.stack || "";
89
+ const message = redactLogString(body.message);
90
+ const stack = redactLogString(originalStack);
91
+ if (message === body.message && stack === originalStack) {
92
+ return body;
93
+ }
94
+ return stack || `${body.name}: ${message}`;
95
+ }
96
+ return redactLogValue(body);
97
+ }
98
+ catch (_a) {
99
+ // Never pass on a body we could not prove was redacted.
100
+ return REDACTED;
101
+ }
102
+ }
103
+ // Serialization only. The body must already have been through redactBody.
104
+ static serializeRedactedBody(body) {
105
+ try {
106
+ if (typeof body === "string") {
107
+ return body;
108
+ }
109
+ else if (body instanceof Exception || body instanceof Error) {
110
+ return body.message;
111
+ }
112
+ /*
113
+ * redactBody swept every string inside this copy already, so stringifying
114
+ * it needs no second textual pass.
115
+ */
116
+ const serialized = JSON.stringify(body);
117
+ return serialized === undefined ? "" : serialized;
70
118
  }
71
- else if (body instanceof Exception || body instanceof Error) {
72
- return body.message;
119
+ catch (_a) {
120
+ return REDACTED;
73
121
  }
74
- return JSON.stringify(body);
122
+ }
123
+ /*
124
+ * Redacts and serializes in one step, for callers outside the level methods
125
+ * that hold a raw body.
126
+ */
127
+ static serializeLogBody(body) {
128
+ return this.serializeRedactedBody(this.redactBody(body));
75
129
  }
76
130
  static sanitizeAttributes(attributes) {
77
131
  if (!attributes) {
@@ -81,7 +135,17 @@ class logger {
81
135
  const sanitized = {};
82
136
  for (const key in attributes) {
83
137
  const value = attributes[key];
84
- if (value !== undefined && value !== null) {
138
+ if (value === undefined || value === null) {
139
+ continue;
140
+ }
141
+ // Attributes are a log sink too, and they travel to telemetry verbatim.
142
+ if (isSensitiveLogKey(key, value)) {
143
+ sanitized[key] = REDACTED;
144
+ }
145
+ else if (typeof value === "string") {
146
+ sanitized[key] = redactLogString(value);
147
+ }
148
+ else {
85
149
  sanitized[key] = value;
86
150
  }
87
151
  }
@@ -94,14 +158,11 @@ class logger {
94
158
  static info(message, attributes) {
95
159
  const logLevel = this.getLogLevel();
96
160
  if (logLevel === ConfigLogLevel.DEBUG || logLevel === ConfigLogLevel.INFO) {
161
+ const body = this.redactBody(message);
97
162
  // eslint-disable-next-line no-console
98
- console.info(message);
99
- this.record("INFO", message);
100
- this.emit({
101
- body: message,
102
- severityNumber: SeverityNumber.INFO,
103
- attributes,
104
- });
163
+ console.info(body);
164
+ this.record("INFO", body);
165
+ this.emitRedacted(body, SeverityNumber.INFO, attributes);
105
166
  }
106
167
  }
107
168
  static error(message, attributes) {
@@ -110,14 +171,11 @@ class logger {
110
171
  logLevel === ConfigLogLevel.INFO ||
111
172
  logLevel === ConfigLogLevel.WARN ||
112
173
  logLevel === ConfigLogLevel.ERROR) {
174
+ const body = this.redactBody(message);
113
175
  // eslint-disable-next-line no-console
114
- console.error(message);
115
- this.record("ERROR", message);
116
- this.emit({
117
- body: message,
118
- severityNumber: SeverityNumber.ERROR,
119
- attributes,
120
- });
176
+ console.error(body);
177
+ this.record("ERROR", body);
178
+ this.emitRedacted(body, SeverityNumber.ERROR, attributes);
121
179
  }
122
180
  }
123
181
  static warn(message, attributes) {
@@ -125,30 +183,28 @@ class logger {
125
183
  if (logLevel === ConfigLogLevel.DEBUG ||
126
184
  logLevel === ConfigLogLevel.INFO ||
127
185
  logLevel === ConfigLogLevel.WARN) {
186
+ const body = this.redactBody(message);
128
187
  // eslint-disable-next-line no-console
129
- console.warn(message);
130
- this.record("WARN", message);
131
- this.emit({
132
- body: message,
133
- severityNumber: SeverityNumber.WARN,
134
- attributes,
135
- });
188
+ console.warn(body);
189
+ this.record("WARN", body);
190
+ this.emitRedacted(body, SeverityNumber.WARN, attributes);
136
191
  }
137
192
  }
138
193
  static debug(message, attributes) {
139
194
  const logLevel = this.getLogLevel();
140
195
  if (logLevel === ConfigLogLevel.DEBUG) {
196
+ const body = this.redactBody(message);
141
197
  // eslint-disable-next-line no-console
142
- console.debug(message);
143
- this.record("DEBUG", message);
144
- this.emit({
145
- body: message,
146
- severityNumber: SeverityNumber.DEBUG,
147
- attributes,
148
- });
198
+ console.debug(body);
199
+ this.record("DEBUG", body);
200
+ this.emitRedacted(body, SeverityNumber.DEBUG, attributes);
149
201
  }
150
202
  }
151
203
  static emit(data) {
204
+ this.emitRedacted(this.redactBody(data.body), data.severityNumber, data.attributes);
205
+ }
206
+ // `body` must already have been through redactBody.
207
+ static emitRedacted(body, severityNumber, attributes) {
152
208
  try {
153
209
  const logger = OneUptimeTelemetry.getLogger();
154
210
  if (logger === null) {
@@ -159,9 +215,9 @@ class logger {
159
215
  * monitorId, requestId, ...) into every log record. Attributes passed
160
216
  * explicitly to the log call take precedence over the ambient context.
161
217
  */
162
- const mergedAttributes = Object.assign(Object.assign({}, TelemetryContext.getAttributes()), (data.attributes || {}));
218
+ const mergedAttributes = Object.assign(Object.assign({}, TelemetryContext.getAttributes()), (attributes || {}));
163
219
  const sanitizedAttributes = this.sanitizeAttributes(mergedAttributes);
164
- logger.emit(Object.assign({ body: this.serializeLogBody(data.body), severityNumber: data.severityNumber }, (sanitizedAttributes ? { attributes: sanitizedAttributes } : {})));
220
+ logger.emit(Object.assign({ body: this.serializeRedactedBody(body), severityNumber: severityNumber }, (sanitizedAttributes ? { attributes: sanitizedAttributes } : {})));
165
221
  }
166
222
  catch (_a) {
167
223
  // Never let logging errors propagate
@@ -170,14 +226,11 @@ class logger {
170
226
  static trace(message, attributes) {
171
227
  const logLevel = this.getLogLevel();
172
228
  if (logLevel === ConfigLogLevel.DEBUG) {
229
+ const body = this.redactBody(message);
173
230
  // eslint-disable-next-line no-console
174
- console.trace(message);
175
- this.record("TRACE", message);
176
- this.emit({
177
- body: message,
178
- severityNumber: SeverityNumber.DEBUG,
179
- attributes,
180
- });
231
+ console.trace(body);
232
+ this.record("TRACE", body);
233
+ this.emitRedacted(body, SeverityNumber.DEBUG, attributes);
181
234
  }
182
235
  }
183
236
  }