@oneuptime/common 12.0.27 → 12.0.29

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 (204) hide show
  1. package/Models/AnalyticsModels/RumSessionChunk.ts +52 -0
  2. package/Models/DatabaseModels/CloudResource.ts +17 -1
  3. package/Models/DatabaseModels/DashboardDomain.ts +43 -0
  4. package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
  5. package/Models/DatabaseModels/RumApplication.ts +35 -2
  6. package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
  7. package/Server/API/DashboardDomainAPI.ts +74 -0
  8. package/Server/API/MicrosoftTeamsAPI.ts +81 -180
  9. package/Server/API/StatusPageDomainAPI.ts +74 -0
  10. package/Server/API/TelemetryAPI.ts +104 -15
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  14. package/Server/Middleware/ProjectAuthorization.ts +6 -1
  15. package/Server/Services/DashboardDomainService.ts +131 -0
  16. package/Server/Services/LogPipelineProcessorService.ts +88 -0
  17. package/Server/Services/MonitorTemplateService.ts +70 -11
  18. package/Server/Services/StatusPageDomainService.ts +163 -3
  19. package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
  20. package/Server/Types/Domain.ts +17 -0
  21. package/Server/Utils/APIKey/AccessPermission.ts +24 -0
  22. package/Server/Utils/Greenlock/Greenlock.ts +156 -58
  23. package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
  24. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
  25. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
  26. package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +12 -5
  27. package/Server/Utils/SessionReplay/SessionReplayIdentity.ts +102 -0
  28. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +11 -2
  29. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
  30. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
  31. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
  32. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
  33. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
  34. package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
  35. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
  36. package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
  37. package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
  38. package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +81 -0
  39. package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
  40. package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
  41. package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
  42. package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
  43. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
  44. package/Tests/Server/API/SessionReplayAPI.test.ts +232 -0
  45. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
  46. package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
  47. package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
  48. package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
  49. package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
  50. package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
  51. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
  52. package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
  53. package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
  54. package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
  55. package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
  56. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
  57. package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
  58. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
  59. package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
  60. package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
  61. package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
  62. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
  63. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
  64. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
  65. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
  66. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
  67. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
  68. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
  69. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
  70. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
  71. package/Tests/Types/API/HTTPResponse.test.ts +175 -0
  72. package/Tests/Types/API/Route.test.ts +36 -4
  73. package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
  74. package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
  75. package/Tests/Types/DateLocalTimeString.test.ts +259 -0
  76. package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
  77. package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
  78. package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
  79. package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
  80. package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
  81. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
  82. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
  83. package/Tests/UI/Utils/Navigation.test.ts +82 -0
  84. package/Tests/Utils/Array.test.ts +100 -0
  85. package/Tests/Utils/CertificateReissue.test.ts +225 -0
  86. package/Tests/Utils/Grok/Grok.test.ts +492 -0
  87. package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
  88. package/Types/API/Route.ts +20 -5
  89. package/Types/Date.ts +143 -13
  90. package/Types/Rum/SessionReplay.ts +76 -1
  91. package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
  92. package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
  93. package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
  94. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
  95. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
  96. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
  97. package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
  98. package/UI/Utils/Navigation.ts +42 -0
  99. package/Utils/Array.ts +37 -0
  100. package/Utils/CertificateReissue.ts +128 -0
  101. package/Utils/Grok/Grok.ts +461 -0
  102. package/Utils/Grok/GrokPatterns.ts +118 -0
  103. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
  104. package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +48 -0
  105. package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -1
  106. package/build/dist/Models/DatabaseModels/CloudResource.js +19 -2
  107. package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
  108. package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
  109. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  110. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
  111. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  112. package/build/dist/Models/DatabaseModels/RumApplication.js +37 -3
  113. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  114. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
  115. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  116. package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
  117. package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
  118. package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
  119. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  120. package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
  121. package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
  122. package/build/dist/Server/API/TelemetryAPI.js +67 -14
  123. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  124. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
  125. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
  126. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
  127. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
  128. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  129. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  130. package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
  131. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  132. package/build/dist/Server/Services/DashboardDomainService.js +104 -0
  133. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  134. package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
  135. package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
  136. package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
  137. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  138. package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
  139. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  140. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
  141. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
  142. package/build/dist/Server/Types/Domain.js +16 -0
  143. package/build/dist/Server/Types/Domain.js.map +1 -1
  144. package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
  145. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  146. package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
  147. package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
  148. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
  149. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
  150. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
  151. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  152. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
  153. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  154. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
  155. package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js +59 -0
  156. package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js.map +1 -0
  157. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +9 -2
  158. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  159. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
  160. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  161. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
  162. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  163. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
  164. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
  165. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
  166. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  167. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
  168. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  169. package/build/dist/Types/API/Route.js +16 -5
  170. package/build/dist/Types/API/Route.js.map +1 -1
  171. package/build/dist/Types/Date.js +115 -13
  172. package/build/dist/Types/Date.js.map +1 -1
  173. package/build/dist/Types/Rum/SessionReplay.js +38 -0
  174. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  175. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
  176. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
  177. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
  178. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  179. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
  180. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
  181. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
  182. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  183. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
  184. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  185. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
  186. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  187. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
  188. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
  189. package/build/dist/UI/Utils/Navigation.js +30 -0
  190. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  191. package/build/dist/Utils/Array.js +25 -0
  192. package/build/dist/Utils/Array.js.map +1 -1
  193. package/build/dist/Utils/CertificateReissue.js +97 -0
  194. package/build/dist/Utils/CertificateReissue.js.map +1 -0
  195. package/build/dist/Utils/Grok/Grok.js +324 -0
  196. package/build/dist/Utils/Grok/Grok.js.map +1 -0
  197. package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
  198. package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
  199. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
  200. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  201. package/package.json +1 -1
  202. package/UI/Utils/JsonWebToken.ts +0 -14
  203. package/build/dist/UI/Utils/JsonWebToken.js +0 -10
  204. package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
@@ -53,8 +53,8 @@ import { beforeAll, describe, expect, test } from "@jest/globals";
53
53
  * getMaxLengthFromTableColumnType returns undefined and the length
54
54
  * check cannot reject a stored error at all,
55
55
  * 2. the widest message the producers can actually emit really is
56
- * accepted — pinned end to end, from the client's own error
57
- * templates through ConnectorErrorMessage.toMessage to the real
56
+ * accepted — pinned end to end, from every one of the client's own
57
+ * failure paths through ConnectorErrorMessage.toMessage to the real
58
58
  * DatabaseService length check,
59
59
  * 3. the migration widens the live Postgres columns with ALTER COLUMN
60
60
  * ... TYPE text rather than DROP + ADD, which would discard every
@@ -108,30 +108,67 @@ const GOOGLE_SECOPS_CLIENT_SOURCE: string = fs.readFileSync(
108
108
  );
109
109
 
110
110
  /*
111
- * How many characters of the Chronicle response body each client error
112
- * template echoes, read out of the client itself rather than restated
113
- * here. Restating it would let the client widen its echo without a single
114
- * test noticing that the stored value grew with it.
111
+ * How many characters of whatever Google sent back the client is willing to
112
+ * echo, read out of the client itself rather than restated here. Restating
113
+ * it would let the client widen its echo without a single test noticing
114
+ * that the stored value grew with it.
115
+ *
116
+ * The premise this file was written on — "there are two error templates and
117
+ * both write `responseText.slice(0, 500)`" — no longer holds. The client
118
+ * reports the failures that arrive on an HTTP 200 as well now (an empty
119
+ * body, a body that is not JSON, a shape the parser does not recognize, a
120
+ * query Chronicle rejected in band), and each of those echoes the thing it
121
+ * could not read through a NAMED constant rather than an inline literal. A
122
+ * scan pinned to the inline form kept passing while it covered less than a
123
+ * third of the sites, which is the failure mode this function exists to
124
+ * prevent. So: every `.slice(0, N)` in the client, named bounds resolved.
115
125
  */
116
126
  function responseBodyEchoLimits(): Array<number> {
117
- const pattern: RegExp = /responseText\.slice\(0,\s*(\d+)\)/g;
127
+ const pattern: RegExp = /\.slice\(\s*0\s*,\s*(\w+)\s*\)/g;
118
128
  const limits: Array<number> = [];
119
129
 
120
130
  let match: RegExpExecArray | null = pattern.exec(GOOGLE_SECOPS_CLIENT_SOURCE);
121
131
  while (match) {
122
- limits.push(Number(match[1]));
132
+ limits.push(resolveNumericToken(String(match[1])));
123
133
  match = pattern.exec(GOOGLE_SECOPS_CLIENT_SOURCE);
124
134
  }
125
135
 
126
136
  return limits;
127
137
  }
128
138
 
139
+ /*
140
+ * An inline bound is its own value; a named one is resolved out of the
141
+ * client's `const NAME: number = N` declaration. An unresolvable name
142
+ * throws rather than becoming NaN and satisfying "they are all equal" by
143
+ * accident.
144
+ */
145
+ const INLINE_BOUND_PATTERN: RegExp = /^\d+$/;
146
+
147
+ function resolveNumericToken(token: string): number {
148
+ if (INLINE_BOUND_PATTERN.test(token)) {
149
+ return Number(token);
150
+ }
151
+
152
+ const declaration: RegExpMatchArray | null =
153
+ GOOGLE_SECOPS_CLIENT_SOURCE.match(
154
+ new RegExp(`\\b${token}\\s*:\\s*number\\s*=\\s*(\\d+)`),
155
+ );
156
+
157
+ if (!declaration || declaration[1] === undefined) {
158
+ throw new Error(
159
+ `GoogleSecOpsClient truncates with .slice(0, ${token}) but declares no numeric ${token} — this file cannot bound what it stores`,
160
+ );
161
+ }
162
+
163
+ return Number(declaration[1]);
164
+ }
165
+
129
166
  function responseBodyEchoLimit(): number {
130
167
  const limits: Array<number> = responseBodyEchoLimits();
131
168
 
132
169
  if (limits.length === 0) {
133
170
  throw new Error(
134
- "GoogleSecOpsClient no longer echoes the response body with responseText.slice(0, N) — this file's premise needs revisiting",
171
+ "GoogleSecOpsClient no longer truncates anything with .slice(0, N) — this file's premise, that every stored error is bounded by a cap the client applies before ConnectorErrorMessage ever sees it, needs revisiting",
135
172
  );
136
173
  }
137
174
 
@@ -144,10 +181,17 @@ const { privateKey } = generateKeyPairSync("rsa", {
144
181
  publicKeyEncoding: { type: "spki", format: "pem" },
145
182
  });
146
183
 
184
+ /*
185
+ * The real token endpoint, not an example.com stand-in: the client now
186
+ * refuses a token_uri outside Google's hosts, because that URL is
187
+ * customer-supplied and the first 500 characters of whatever answers it are
188
+ * echoed straight into lastError. Nothing here reaches the network — the
189
+ * fetch is injected — so the host only has to satisfy that check.
190
+ */
147
191
  const SERVICE_ACCOUNT_JSON: string = JSON.stringify({
148
192
  client_email: "poller@example.iam.gserviceaccount.com",
149
193
  private_key: privateKey,
150
- token_uri: "https://oauth2.example.com/token",
194
+ token_uri: "https://oauth2.googleapis.com/token",
151
195
  });
152
196
 
153
197
  const INSTANCE: string =
@@ -400,13 +444,21 @@ describe("the widest error the producers can emit is storable", () => {
400
444
  : tokenExchangeError;
401
445
  });
402
446
 
403
- test("the client has exactly two error templates and both echo the body under the same cap", () => {
447
+ test("every body the client echoes is capped at the same limit", () => {
448
+ /*
449
+ * The premise, restated honestly. It is no longer "exactly two
450
+ * templates" — the count is whatever the client has, and what must hold
451
+ * is that all of them truncate at one number. A single site echoing
452
+ * further than the rest would widen the stored value without widening
453
+ * anything that bounds it, and the two-site scan this replaced would
454
+ * not have seen it: four of the seven sites did not exist when that
455
+ * assertion was written.
456
+ */
404
457
  const limits: Array<number> = responseBodyEchoLimits();
405
458
 
406
- expect(limits).toHaveLength(2);
407
- for (const limit of limits) {
408
- expect(limit).toBe(responseBodyEchoLimit());
409
- }
459
+ expect(limits.length).toBeGreaterThan(2);
460
+ expect(new Set(limits).size).toBe(1);
461
+ expect(responseBodyEchoLimit()).toBe(limits[0]);
410
462
  });
411
463
 
412
464
  test("each template is a fixed prefix plus a full-width echo of the response body", () => {
@@ -540,6 +592,90 @@ describe("the widest error the producers can emit is storable", () => {
540
592
  });
541
593
  });
542
594
 
595
+ interface InBandFailure {
596
+ label: string;
597
+ /* The 200 body Chronicle answers with. */
598
+ body: string;
599
+ /* Everything the client's message carries ahead of the echoed text. */
600
+ prefix: string;
601
+ }
602
+
603
+ /*
604
+ * The failures Chronicle reports on an HTTP 200. They are not covered by the
605
+ * two templates above because before the client learned to read the stream
606
+ * envelope they did not reach lastError at all — an unreadable body was
607
+ * reported as a healthy poll that found nothing. Each of them now echoes the
608
+ * text it could not read, which is a new way for the stored value to grow,
609
+ * so each has to clear the same end-to-end path: the client's own cap, the
610
+ * connector clamp, and the real length check on both columns.
611
+ */
612
+ const IN_BAND_FAILURES: Array<InBandFailure> = [
613
+ {
614
+ label: "an empty body",
615
+ body: "",
616
+ prefix: "Google SecOps alerts fetch returned an empty body.",
617
+ },
618
+ {
619
+ label: "a body that is not JSON",
620
+ body: `<html>${OVERSIZED_RESPONSE_BODY}</html>`,
621
+ prefix: "Google SecOps alerts fetch returned a non-JSON body.",
622
+ },
623
+ {
624
+ label: "a shape the parser does not recognize",
625
+ body: JSON.stringify([{ somethingElseEntirely: OVERSIZED_RESPONSE_BODY }]),
626
+ prefix:
627
+ "Google SecOps alerts fetch returned an unrecognized response shape: ",
628
+ },
629
+ {
630
+ label: "a query Chronicle rejected in band",
631
+ body: JSON.stringify([
632
+ {
633
+ validSnapshotQuery: false,
634
+ queryValidationErrors: [{ message: OVERSIZED_RESPONSE_BODY }],
635
+ },
636
+ ]),
637
+ prefix:
638
+ "Google SecOps alerts query was rejected by Chronicle on an HTTP 200: ",
639
+ },
640
+ ];
641
+
642
+ describe("the failures Chronicle reports on an HTTP 200 are storable too", () => {
643
+ test.each(IN_BAND_FAILURES)(
644
+ "$label is echoed under the client's own cap and stored whole",
645
+ async (failure: InBandFailure) => {
646
+ const error: Error = await thrownClientError([
647
+ successfulTokenResponse(),
648
+ { status: 200, body: failure.body },
649
+ ]);
650
+
651
+ expect(error.message.startsWith(failure.prefix)).toBe(true);
652
+
653
+ /*
654
+ * Prefix plus at most one full-width echo — the same shape as the
655
+ * HTTP templates, so nothing here is wider than the case the column
656
+ * was widened for.
657
+ */
658
+ expect(error.message.length).toBeLessThanOrEqual(
659
+ failure.prefix.length + responseBodyEchoLimit(),
660
+ );
661
+
662
+ // The clamp leaves it untouched, so lastError carries the whole thing.
663
+ const stored: string = ConnectorErrorMessage.toMessage(error);
664
+
665
+ expect(stored).toBe(error.message);
666
+ expect(stored.length).toBeLessThanOrEqual(
667
+ MAX_CONNECTOR_ERROR_MESSAGE_LENGTH,
668
+ );
669
+
670
+ for (const column of LAST_ERROR_COLUMNS) {
671
+ expect(() => {
672
+ return column.checkLastError(stored);
673
+ }).not.toThrow();
674
+ }
675
+ },
676
+ );
677
+ });
678
+
543
679
  describe("WidenSecurityEventLastErrorColumns1789800000000 SQL contract", () => {
544
680
  const migration: WidenSecurityEventLastErrorColumns1789800000000 =
545
681
  new WidenSecurityEventLastErrorColumns1789800000000();