@oneuptime/common 12.0.28 → 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 (182) hide show
  1. package/Models/DatabaseModels/DashboardDomain.ts +43 -0
  2. package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
  3. package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
  4. package/Server/API/DashboardDomainAPI.ts +74 -0
  5. package/Server/API/MicrosoftTeamsAPI.ts +81 -180
  6. package/Server/API/StatusPageDomainAPI.ts +74 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  10. package/Server/Middleware/ProjectAuthorization.ts +6 -1
  11. package/Server/Services/DashboardDomainService.ts +131 -0
  12. package/Server/Services/LogPipelineProcessorService.ts +88 -0
  13. package/Server/Services/MonitorTemplateService.ts +70 -11
  14. package/Server/Services/StatusPageDomainService.ts +163 -3
  15. package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
  16. package/Server/Types/Domain.ts +17 -0
  17. package/Server/Utils/APIKey/AccessPermission.ts +24 -0
  18. package/Server/Utils/Greenlock/Greenlock.ts +156 -58
  19. package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
  20. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
  21. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
  22. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
  23. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
  24. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
  25. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
  26. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
  27. package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
  28. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
  29. package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
  30. package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
  31. package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
  32. package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
  33. package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
  34. package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
  35. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
  36. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
  37. package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
  38. package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
  39. package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
  40. package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
  41. package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
  42. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
  43. package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
  44. package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
  45. package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
  46. package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
  47. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
  48. package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
  49. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
  50. package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
  51. package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
  52. package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
  53. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
  54. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
  55. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
  56. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
  57. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
  58. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
  59. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
  60. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
  61. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
  62. package/Tests/Types/API/HTTPResponse.test.ts +175 -0
  63. package/Tests/Types/API/Route.test.ts +36 -4
  64. package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
  65. package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
  66. package/Tests/Types/DateLocalTimeString.test.ts +259 -0
  67. package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
  68. package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
  69. package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
  70. package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
  71. package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
  72. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
  73. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
  74. package/Tests/UI/Utils/Navigation.test.ts +82 -0
  75. package/Tests/Utils/Array.test.ts +100 -0
  76. package/Tests/Utils/CertificateReissue.test.ts +225 -0
  77. package/Tests/Utils/Grok/Grok.test.ts +492 -0
  78. package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
  79. package/Types/API/Route.ts +20 -5
  80. package/Types/Date.ts +143 -13
  81. package/Types/Rum/SessionReplay.ts +62 -1
  82. package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
  83. package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
  84. package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
  85. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
  86. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
  87. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
  88. package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
  89. package/UI/Utils/Navigation.ts +42 -0
  90. package/Utils/Array.ts +37 -0
  91. package/Utils/CertificateReissue.ts +128 -0
  92. package/Utils/Grok/Grok.ts +461 -0
  93. package/Utils/Grok/GrokPatterns.ts +118 -0
  94. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
  95. package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
  96. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  97. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
  98. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  99. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
  100. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  101. package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
  102. package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
  103. package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
  104. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  105. package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
  106. package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
  111. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  112. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  113. package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
  114. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  115. package/build/dist/Server/Services/DashboardDomainService.js +104 -0
  116. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  117. package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
  118. package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
  119. package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
  120. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  121. package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
  122. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  123. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
  124. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
  125. package/build/dist/Server/Types/Domain.js +16 -0
  126. package/build/dist/Server/Types/Domain.js.map +1 -1
  127. package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
  128. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  129. package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
  130. package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
  131. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
  132. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
  133. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
  134. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  135. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
  136. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  137. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
  138. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  139. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
  140. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  141. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
  142. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
  143. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
  144. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  145. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
  146. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  147. package/build/dist/Types/API/Route.js +16 -5
  148. package/build/dist/Types/API/Route.js.map +1 -1
  149. package/build/dist/Types/Date.js +115 -13
  150. package/build/dist/Types/Date.js.map +1 -1
  151. package/build/dist/Types/Rum/SessionReplay.js +38 -0
  152. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  153. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
  154. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
  155. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
  156. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  157. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
  158. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
  159. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
  160. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  161. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
  162. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  163. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
  164. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  165. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
  166. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
  167. package/build/dist/UI/Utils/Navigation.js +30 -0
  168. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  169. package/build/dist/Utils/Array.js +25 -0
  170. package/build/dist/Utils/Array.js.map +1 -1
  171. package/build/dist/Utils/CertificateReissue.js +97 -0
  172. package/build/dist/Utils/CertificateReissue.js.map +1 -0
  173. package/build/dist/Utils/Grok/Grok.js +324 -0
  174. package/build/dist/Utils/Grok/Grok.js.map +1 -0
  175. package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
  176. package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
  177. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
  178. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  179. package/package.json +1 -1
  180. package/UI/Utils/JsonWebToken.ts +0 -14
  181. package/build/dist/UI/Utils/JsonWebToken.js +0 -10
  182. package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
@@ -0,0 +1,175 @@
1
+ import HTTPResponse from "../../../Types/API/HTTPResponse";
2
+ import { JSONArray, JSONObject } from "../../../Types/JSON";
3
+ import { describe, expect, it } from "@jest/globals";
4
+
5
+ /*
6
+ * HTTPResponse's constructor picks one of four shapes for the incoming body
7
+ * (paginated list, plain array, string, plain object) and its status-code
8
+ * predicates classify the response. Response.test.ts covers only the happy
9
+ * object/array path, so the list-detection branch, the hasMore flag, the
10
+ * string branch, and every predicate were previously untested.
11
+ */
12
+ describe("HTTPResponse", () => {
13
+ describe("constructor - plain object body", () => {
14
+ it("stores a single object as jsonData and data", () => {
15
+ const response: HTTPResponse<JSONObject> = new HTTPResponse(
16
+ 200,
17
+ { name: "oneuptime" },
18
+ {},
19
+ );
20
+
21
+ expect(response.statusCode).toBe(200);
22
+ expect(response.jsonData).toEqual({ name: "oneuptime" });
23
+ expect(response.data).toEqual({ name: "oneuptime" });
24
+ // A plain object body is not a list, so pagination stays at defaults.
25
+ expect(response.count).toBe(0);
26
+ expect(response.skip).toBe(0);
27
+ expect(response.limit).toBe(0);
28
+ expect(response.hasMore).toBeUndefined();
29
+ });
30
+
31
+ it("keeps the provided headers", () => {
32
+ const response: HTTPResponse<JSONObject> = new HTTPResponse(
33
+ 200,
34
+ { ok: true },
35
+ { "content-type": "application/json" },
36
+ );
37
+
38
+ expect(response.headers).toEqual({ "content-type": "application/json" });
39
+ });
40
+ });
41
+
42
+ describe("constructor - array body", () => {
43
+ it("stores an array as jsonData without touching pagination", () => {
44
+ const response: HTTPResponse<Array<JSONObject>> = new HTTPResponse(
45
+ 200,
46
+ [{ id: 1 }, { id: 2 }],
47
+ {},
48
+ );
49
+
50
+ expect(response.jsonData).toEqual([{ id: 1 }, { id: 2 }]);
51
+ expect(response.data).toEqual([{ id: 1 }, { id: 2 }]);
52
+ expect(response.count).toBe(0);
53
+ });
54
+ });
55
+
56
+ describe("constructor - paginated list body", () => {
57
+ it("extracts count, skip, limit and the nested data array", () => {
58
+ const listData: JSONObject = {
59
+ count: 42,
60
+ skip: 10,
61
+ limit: 20,
62
+ data: [{ id: "a" }, { id: "b" }] as JSONArray,
63
+ };
64
+
65
+ const response: HTTPResponse<Array<JSONObject>> = new HTTPResponse(
66
+ 200,
67
+ listData,
68
+ {},
69
+ );
70
+
71
+ expect(response.count).toBe(42);
72
+ expect(response.skip).toBe(10);
73
+ expect(response.limit).toBe(20);
74
+ expect(response.jsonData).toEqual([{ id: "a" }, { id: "b" }]);
75
+ // hasMore is absent from the body, so it must remain undefined.
76
+ expect(response.hasMore).toBeUndefined();
77
+ });
78
+
79
+ it("reads hasMore when the list body carries it", () => {
80
+ const listData: JSONObject = {
81
+ count: 5,
82
+ skip: 0,
83
+ limit: 5,
84
+ hasMore: true,
85
+ data: [] as JSONArray,
86
+ };
87
+
88
+ const response: HTTPResponse<Array<JSONObject>> = new HTTPResponse(
89
+ 200,
90
+ listData,
91
+ {},
92
+ );
93
+
94
+ expect(response.hasMore).toBe(true);
95
+ expect(response.jsonData).toEqual([]);
96
+ });
97
+
98
+ it("preserves hasMore === false as a real value, not a default", () => {
99
+ const listData: JSONObject = {
100
+ count: 3,
101
+ skip: 0,
102
+ limit: 10,
103
+ hasMore: false,
104
+ data: [{ id: 1 }] as JSONArray,
105
+ };
106
+
107
+ const response: HTTPResponse<Array<JSONObject>> = new HTTPResponse(
108
+ 200,
109
+ listData,
110
+ {},
111
+ );
112
+
113
+ expect(response.hasMore).toBe(false);
114
+ });
115
+
116
+ it("does not treat an object missing one pagination key as a list", () => {
117
+ // count + skip but no limit => plain object branch, no pagination parse.
118
+ const response: HTTPResponse<JSONObject> = new HTTPResponse(
119
+ 200,
120
+ { count: 9, skip: 0, data: [{ id: 1 }] as JSONArray },
121
+ {},
122
+ );
123
+
124
+ expect(response.count).toBe(0);
125
+ expect(response.skip).toBe(0);
126
+ expect(response.limit).toBe(0);
127
+ // The whole object is kept as jsonData since it was not a list.
128
+ expect(response.jsonData).toEqual({
129
+ count: 9,
130
+ skip: 0,
131
+ data: [{ id: 1 }],
132
+ });
133
+ });
134
+ });
135
+
136
+ describe("status code predicates", () => {
137
+ const makeResponse: (statusCode: number) => HTTPResponse<JSONObject> = (
138
+ statusCode: number,
139
+ ): HTTPResponse<JSONObject> => {
140
+ return new HTTPResponse<JSONObject>(statusCode, { ok: true }, {});
141
+ };
142
+
143
+ it("isSuccess is true only for 200", () => {
144
+ expect(makeResponse(200).isSuccess()).toBe(true);
145
+ expect(makeResponse(201).isSuccess()).toBe(false);
146
+ expect(makeResponse(404).isSuccess()).toBe(false);
147
+ });
148
+
149
+ it("isFailure is the inverse of a 200 response", () => {
150
+ expect(makeResponse(200).isFailure()).toBe(false);
151
+ expect(makeResponse(500).isFailure()).toBe(true);
152
+ expect(makeResponse(201).isFailure()).toBe(true);
153
+ });
154
+
155
+ it("isNotAuthorized is true only for 401", () => {
156
+ expect(makeResponse(401).isNotAuthorized()).toBe(true);
157
+ expect(makeResponse(403).isNotAuthorized()).toBe(false);
158
+ });
159
+
160
+ it("isTooManyRequests is true only for 429", () => {
161
+ expect(makeResponse(429).isTooManyRequests()).toBe(true);
162
+ expect(makeResponse(400).isTooManyRequests()).toBe(false);
163
+ });
164
+
165
+ it("isPaymentDeclined is true only for 402", () => {
166
+ expect(makeResponse(402).isPaymentDeclined()).toBe(true);
167
+ expect(makeResponse(200).isPaymentDeclined()).toBe(false);
168
+ });
169
+
170
+ it("isServerError is true only for 500", () => {
171
+ expect(makeResponse(500).isServerError()).toBe(true);
172
+ expect(makeResponse(502).isServerError()).toBe(false);
173
+ });
174
+ });
175
+ });
@@ -2,7 +2,7 @@ import Route from "../../../Types/API/Route";
2
2
  import BadDataException from "../../../Types/Exception/BadDataException";
3
3
 
4
4
  describe("Route", () => {
5
- test("new Route() should throw an error if invalid route is passed", () => {
5
+ test("new Route() should throw an error if invalid characters are passed", () => {
6
6
  expect(() => {
7
7
  return new Route("api test");
8
8
  }).toThrowError(BadDataException);
@@ -16,15 +16,47 @@ describe("Route", () => {
16
16
  return new Route("/api|test");
17
17
  }).toThrowError(BadDataException);
18
18
  });
19
- test("new Route() should throw an error if invalid route is passed", () => {
20
- const route: Route = new Route("/api/test");
19
+
20
+ test.each([
21
+ "javascript:alert(document.domain)",
22
+ "JaVaScRiPt:alert(1)",
23
+ "data:text/plain,hello",
24
+ "vbscript:msgbox(1)",
25
+ "file:/etc/passwd",
26
+ "http://evil.example/path",
27
+ "https://evil.example/path",
28
+ "mailto:user@example.com",
29
+ "custom+scheme:value",
30
+ ])("rejects scheme-like route %s", (routeValue: string) => {
21
31
  expect(() => {
22
- route.route = "api`test";
32
+ return new Route(routeValue);
23
33
  }).toThrowError(BadDataException);
24
34
  });
35
+
36
+ test.each(["api`test", "javascript:alert(document.domain)"])(
37
+ "route assignment rejects invalid value %s",
38
+ (routeValue: string) => {
39
+ const route: Route = new Route("/api/test");
40
+
41
+ expect(() => {
42
+ route.route = routeValue;
43
+ }).toThrowError(BadDataException);
44
+ },
45
+ );
46
+
25
47
  test("Route.toString() should return valid string", () => {
26
48
  expect(new Route("/api/test").toString()).toBe("/api/test");
27
49
  expect(new Route("/api#test").toString()).toBe("/api#test");
28
50
  expect(new Route("/api-test").toString()).toBe("/api-test");
29
51
  });
52
+
53
+ test.each([
54
+ "/status-page/:statusPageId/incidents",
55
+ "api/:resourceId",
56
+ "/events?at=12:30",
57
+ "/callback?next=custom:value",
58
+ "/literal/javascript:alert(1)",
59
+ ])("preserves legitimate colon use in %s", (routeValue: string) => {
60
+ expect(new Route(routeValue).toString()).toBe(routeValue);
61
+ });
30
62
  });
@@ -0,0 +1,171 @@
1
+ import DatabaseProperty from "../../../Types/Database/DatabaseProperty";
2
+ import NotImplementedException from "../../../Types/Exception/NotImplementedException";
3
+ import { JSONArray, JSONObject } from "../../../Types/JSON";
4
+ import { describe, expect, it } from "@jest/globals";
5
+ import { Equal, FindOperator, In, Not } from "typeorm";
6
+ import { ValueTransformer } from "typeorm/decorator/options/ValueTransformer";
7
+
8
+ /*
9
+ * DatabaseProperty is the base every value-object column type extends. Its
10
+ * TypeORM ValueTransformer carries three pieces of behavior that are easy to
11
+ * break and expensive to break:
12
+ *
13
+ * - `to(undefined)` must stay `undefined` so an omitted column falls to its
14
+ * SQL DEFAULT instead of being rewritten to an explicit NULL (a not-null
15
+ * constraint violation / HTTP 500 on every create otherwise).
16
+ * - a Raw query object must pass straight through untouched.
17
+ * - a FindOperator (Equal/In/Not/And/...) must keep its structure while its
18
+ * leaf value(s) are converted, so a privacy/scope clause is preserved.
19
+ *
20
+ * DatabasePropertyFindOperator.test.ts checks the concrete subclasses. This
21
+ * suite pins the base-class dispatch itself, using a tiny subclass whose
22
+ * conversion is observable (uppercasing).
23
+ */
24
+
25
+ // A minimal concrete DatabaseProperty whose conversions are observable.
26
+ class UpperCaseProperty extends DatabaseProperty {
27
+ public value: string;
28
+
29
+ public constructor(value: string) {
30
+ super();
31
+ this.value = value;
32
+ }
33
+
34
+ protected static override fromDatabase(
35
+ value: string | number | JSONObject | JSONArray,
36
+ ): DatabaseProperty | Array<DatabaseProperty> | null {
37
+ return new UpperCaseProperty(String(value));
38
+ }
39
+
40
+ protected static override toDatabase(
41
+ value: DatabaseProperty | Array<DatabaseProperty> | any,
42
+ ): string | number | JSONObject | JSONArray | null {
43
+ if (value instanceof UpperCaseProperty) {
44
+ return value.value.toUpperCase();
45
+ }
46
+
47
+ if (typeof value === "string") {
48
+ return value.toUpperCase();
49
+ }
50
+
51
+ return null;
52
+ }
53
+ }
54
+
55
+ describe("DatabaseProperty", () => {
56
+ describe("getDatabaseTransformer().to", () => {
57
+ it("passes undefined through unchanged so the SQL DEFAULT survives", () => {
58
+ const transformer: ValueTransformer =
59
+ UpperCaseProperty.getDatabaseTransformer();
60
+
61
+ // The whole point: undefined must NOT become null here.
62
+ expect(transformer.to(undefined)).toBeUndefined();
63
+ });
64
+
65
+ it("delegates a plain value to the subclass toDatabase", () => {
66
+ const transformer: ValueTransformer =
67
+ UpperCaseProperty.getDatabaseTransformer();
68
+
69
+ expect(transformer.to(new UpperCaseProperty("hello"))).toBe("HELLO");
70
+ });
71
+
72
+ it("passes a raw query object straight through without conversion", () => {
73
+ const transformer: ValueTransformer =
74
+ UpperCaseProperty.getDatabaseTransformer();
75
+
76
+ const raw: JSONObject = { _type: "raw", value: "keep me as-is" };
77
+ const result: unknown = transformer.to(raw);
78
+
79
+ // Same object, untouched - toDatabase (which uppercases) never ran.
80
+ expect(result).toBe(raw);
81
+ expect((result as JSONObject)["value"]).toBe("keep me as-is");
82
+ });
83
+
84
+ it("preserves an Equal FindOperator and converts only its leaf", () => {
85
+ const transformer: ValueTransformer =
86
+ UpperCaseProperty.getDatabaseTransformer();
87
+
88
+ const operator: FindOperator<string> = Equal("abc");
89
+ const result: unknown = transformer.to(operator);
90
+
91
+ // Structure kept (same instance), leaf converted.
92
+ expect(result).toBe(operator);
93
+ expect((result as FindOperator<string>).value).toBe("ABC");
94
+ });
95
+
96
+ it("preserves a Not FindOperator and converts its leaf", () => {
97
+ const transformer: ValueTransformer =
98
+ UpperCaseProperty.getDatabaseTransformer();
99
+
100
+ const operator: FindOperator<string> = Not("nope");
101
+ const result: unknown = transformer.to(operator);
102
+
103
+ expect(result).toBe(operator);
104
+ expect((result as FindOperator<string>).value).toBe("NOPE");
105
+ });
106
+
107
+ it("converts every leaf of a multi-value In FindOperator", () => {
108
+ const transformer: ValueTransformer =
109
+ UpperCaseProperty.getDatabaseTransformer();
110
+
111
+ const operator: FindOperator<Array<string>> = In(["a", "b", "c"]);
112
+ const result: unknown = transformer.to(operator);
113
+
114
+ expect(result).toBe(operator);
115
+ expect((result as FindOperator<Array<string>>).value).toEqual([
116
+ "A",
117
+ "B",
118
+ "C",
119
+ ]);
120
+ });
121
+
122
+ it("is a fixpoint: transforming its own output again is a no-op", () => {
123
+ const transformer: ValueTransformer =
124
+ UpperCaseProperty.getDatabaseTransformer();
125
+
126
+ const operator: FindOperator<string> = Equal("abc");
127
+ transformer.to(operator);
128
+ // Second pass (BaseAPI.getList shares the query between findBy/countBy).
129
+ transformer.to(operator);
130
+
131
+ expect(operator.value).toBe("ABC");
132
+ });
133
+ });
134
+
135
+ describe("getDatabaseTransformer().from", () => {
136
+ it("delegates to the subclass fromDatabase", () => {
137
+ const transformer: ValueTransformer =
138
+ UpperCaseProperty.getDatabaseTransformer();
139
+
140
+ const result: unknown = transformer.from("stored");
141
+
142
+ expect(result).toBeInstanceOf(UpperCaseProperty);
143
+ expect((result as UpperCaseProperty).value).toBe("stored");
144
+ });
145
+ });
146
+
147
+ describe("base class defaults", () => {
148
+ it("getSchema throws NotImplementedException on the base class", () => {
149
+ expect(() => {
150
+ return DatabaseProperty.getSchema();
151
+ }).toThrow(NotImplementedException);
152
+ });
153
+
154
+ it("still short-circuits undefined even on the un-implemented base", () => {
155
+ const transformer: ValueTransformer =
156
+ DatabaseProperty.getDatabaseTransformer();
157
+
158
+ // Reaches the undefined guard before the un-implemented toDatabase.
159
+ expect(transformer.to(undefined)).toBeUndefined();
160
+ });
161
+
162
+ it("throws NotImplementedException when the base must actually convert", () => {
163
+ const transformer: ValueTransformer =
164
+ DatabaseProperty.getDatabaseTransformer();
165
+
166
+ expect(() => {
167
+ return transformer.to("a real value");
168
+ }).toThrow(NotImplementedException);
169
+ });
170
+ });
171
+ });