@reclaimprotocol/attestor-core 5.0.1-beta.2 → 5.0.1-beta.22

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 (145) hide show
  1. package/browser/resources/attestor-browser.min.mjs +4512 -0
  2. package/lib/avs/abis/avsDirectoryABI.js +338 -341
  3. package/lib/avs/abis/delegationABI.js +1 -4
  4. package/lib/avs/abis/registryABI.js +719 -722
  5. package/lib/avs/client/create-claim-on-avs.js +129 -157
  6. package/lib/avs/config.js +18 -24
  7. package/lib/avs/contracts/ReclaimServiceManager.js +1 -0
  8. package/lib/avs/contracts/common.js +1 -0
  9. package/lib/avs/contracts/factories/ReclaimServiceManager__factory.js +1139 -1156
  10. package/lib/avs/contracts/factories/index.js +4 -4
  11. package/lib/avs/contracts/index.js +2 -6
  12. package/lib/avs/types/index.js +1 -0
  13. package/lib/avs/utils/contracts.js +30 -50
  14. package/lib/avs/utils/register.js +75 -70
  15. package/lib/avs/utils/tasks.js +38 -45
  16. package/lib/client/create-claim.js +402 -431
  17. package/lib/client/tunnels/make-rpc-tcp-tunnel.js +46 -48
  18. package/lib/client/tunnels/make-rpc-tls-tunnel.js +125 -121
  19. package/lib/client/utils/attestor-pool.js +23 -22
  20. package/lib/client/utils/client-socket.js +86 -109
  21. package/lib/client/utils/message-handler.js +79 -89
  22. package/lib/config/index.js +40 -58
  23. package/lib/external-rpc/benchmark.js +61 -74
  24. package/lib/external-rpc/event-bus.js +12 -15
  25. package/lib/external-rpc/handle-incoming-msg.js +216 -225
  26. package/lib/external-rpc/jsc-polyfills/1.js +70 -68
  27. package/lib/external-rpc/jsc-polyfills/2.js +17 -12
  28. package/lib/external-rpc/jsc-polyfills/event.js +10 -15
  29. package/lib/external-rpc/jsc-polyfills/index.js +2 -2
  30. package/lib/external-rpc/jsc-polyfills/ws.js +77 -79
  31. package/lib/external-rpc/setup-browser.js +28 -28
  32. package/lib/external-rpc/setup-jsc.js +17 -17
  33. package/lib/external-rpc/types.js +1 -0
  34. package/lib/external-rpc/utils.js +89 -89
  35. package/lib/external-rpc/zk.js +55 -50
  36. package/lib/index.js +2 -6
  37. package/lib/mechain/abis/governanceABI.js +457 -460
  38. package/lib/mechain/abis/taskABI.js +502 -505
  39. package/lib/mechain/client/create-claim-on-mechain.js +24 -29
  40. package/lib/mechain/constants/index.js +3 -8
  41. package/lib/mechain/types/index.js +1 -0
  42. package/lib/proto/api.js +4200 -4087
  43. package/lib/proto/tee-bundle.js +1261 -1241
  44. package/lib/providers/http/index.js +616 -603
  45. package/lib/providers/http/patch-parse5-tree.js +27 -29
  46. package/lib/providers/http/utils.js +289 -248
  47. package/lib/providers/index.js +3 -6
  48. package/lib/server/create-server.js +89 -91
  49. package/lib/server/handlers/claimTeeBundle.js +231 -211
  50. package/lib/server/handlers/claimTunnel.js +66 -73
  51. package/lib/server/handlers/completeClaimOnChain.js +20 -25
  52. package/lib/server/handlers/createClaimOnChain.js +21 -27
  53. package/lib/server/handlers/createTaskOnMechain.js +40 -50
  54. package/lib/server/handlers/createTunnel.js +85 -90
  55. package/lib/server/handlers/disconnectTunnel.js +4 -7
  56. package/lib/server/handlers/fetchCertificateBytes.js +37 -53
  57. package/lib/server/handlers/index.js +21 -24
  58. package/lib/server/handlers/init.js +27 -28
  59. package/lib/server/handlers/toprf.js +13 -16
  60. package/lib/server/socket.js +97 -100
  61. package/lib/server/tunnels/make-tcp-tunnel.js +161 -186
  62. package/lib/server/utils/apm.js +32 -25
  63. package/lib/server/utils/assert-valid-claim-request.js +305 -334
  64. package/lib/server/utils/config-env.js +2 -2
  65. package/lib/server/utils/dns.js +12 -18
  66. package/lib/server/utils/gcp-attestation.js +233 -181
  67. package/lib/server/utils/generics.d.ts +1 -1
  68. package/lib/server/utils/generics.js +43 -37
  69. package/lib/server/utils/iso.js +253 -256
  70. package/lib/server/utils/keep-alive.js +36 -36
  71. package/lib/server/utils/nitro-attestation.js +295 -220
  72. package/lib/server/utils/oprf-raw.js +48 -55
  73. package/lib/server/utils/process-handshake.js +200 -218
  74. package/lib/server/utils/proxy-session.js +5 -5
  75. package/lib/server/utils/tee-oprf-mpc-verification.js +82 -78
  76. package/lib/server/utils/tee-oprf-verification.js +165 -142
  77. package/lib/server/utils/tee-transcript-reconstruction.js +176 -129
  78. package/lib/server/utils/tee-verification.js +397 -334
  79. package/lib/server/utils/validation.js +30 -37
  80. package/lib/types/bgp.js +1 -0
  81. package/lib/types/claims.js +1 -0
  82. package/lib/types/client.js +1 -0
  83. package/lib/types/general.js +1 -0
  84. package/lib/types/handlers.js +1 -0
  85. package/lib/types/providers.d.ts +3 -2
  86. package/lib/types/providers.gen.js +9 -15
  87. package/lib/types/providers.js +1 -0
  88. package/lib/types/rpc.js +1 -0
  89. package/lib/types/signatures.d.ts +1 -2
  90. package/lib/types/signatures.js +1 -0
  91. package/lib/types/tunnel.js +1 -0
  92. package/lib/types/zk.js +1 -0
  93. package/lib/utils/auth.js +54 -66
  94. package/lib/utils/b64-json.js +15 -15
  95. package/lib/utils/bgp-listener.js +107 -111
  96. package/lib/utils/claims.js +89 -80
  97. package/lib/utils/env.js +13 -17
  98. package/lib/utils/error.js +43 -47
  99. package/lib/utils/generics.js +284 -235
  100. package/lib/utils/http-parser.js +232 -187
  101. package/lib/utils/logger.js +80 -71
  102. package/lib/utils/prepare-packets.js +69 -67
  103. package/lib/utils/redactions.js +163 -121
  104. package/lib/utils/retries.js +22 -24
  105. package/lib/utils/signatures/eth.js +29 -28
  106. package/lib/utils/signatures/index.js +5 -10
  107. package/lib/utils/socket-base.js +84 -88
  108. package/lib/utils/tls.js +28 -28
  109. package/lib/utils/ws.js +19 -19
  110. package/lib/utils/zk.js +542 -582
  111. package/package.json +12 -5
  112. package/lib/external-rpc/global.d.js +0 -0
  113. package/lib/scripts/build-browser.d.ts +0 -1
  114. package/lib/scripts/build-jsc.d.ts +0 -1
  115. package/lib/scripts/build-lib.d.ts +0 -1
  116. package/lib/scripts/check-avs-registration.d.ts +0 -1
  117. package/lib/scripts/check-avs-registration.js +0 -28
  118. package/lib/scripts/fallbacks/crypto.d.ts +0 -1
  119. package/lib/scripts/fallbacks/crypto.js +0 -4
  120. package/lib/scripts/fallbacks/empty.d.ts +0 -3
  121. package/lib/scripts/fallbacks/empty.js +0 -4
  122. package/lib/scripts/fallbacks/re2.d.ts +0 -1
  123. package/lib/scripts/fallbacks/re2.js +0 -7
  124. package/lib/scripts/fallbacks/snarkjs.d.ts +0 -1
  125. package/lib/scripts/fallbacks/snarkjs.js +0 -10
  126. package/lib/scripts/fallbacks/stwo.d.ts +0 -6
  127. package/lib/scripts/fallbacks/stwo.js +0 -159
  128. package/lib/scripts/generate-provider-types.d.ts +0 -5
  129. package/lib/scripts/generate-provider-types.js +0 -101
  130. package/lib/scripts/generate-receipt.d.ts +0 -9
  131. package/lib/scripts/generate-receipt.js +0 -101
  132. package/lib/scripts/generate-toprf-keys.d.ts +0 -1
  133. package/lib/scripts/generate-toprf-keys.js +0 -24
  134. package/lib/scripts/jsc-cli-rpc.d.ts +0 -1
  135. package/lib/scripts/jsc-cli-rpc.js +0 -35
  136. package/lib/scripts/register-avs-operator.d.ts +0 -1
  137. package/lib/scripts/register-avs-operator.js +0 -3
  138. package/lib/scripts/start-server.d.ts +0 -1
  139. package/lib/scripts/start-server.js +0 -11
  140. package/lib/scripts/update-avs-metadata.d.ts +0 -1
  141. package/lib/scripts/update-avs-metadata.js +0 -20
  142. package/lib/scripts/utils.d.ts +0 -1
  143. package/lib/scripts/utils.js +0 -10
  144. package/lib/scripts/whitelist-operator.d.ts +0 -1
  145. package/lib/scripts/whitelist-operator.js +0 -16
@@ -1,640 +1,653 @@
1
- import { areUint8ArraysEqual, concatenateUint8Arrays, uint8ArrayToBinaryStr } from "@reclaimprotocol/tls";
2
- import { encodeBase64 } from "ethers";
1
+ import { areUint8ArraysEqual, concatenateUint8Arrays, uint8ArrayToBinaryStr } from '@reclaimprotocol/tls';
2
+ import { encodeBase64 } from 'ethers';
3
3
  import { DEFAULT_HTTPS_PORT, RECLAIM_USER_AGENT } from "../../config/index.js";
4
4
  import { AttestorVersion } from "../../proto/api.js";
5
- import {
6
- buildHeaders,
7
- convertResponsePosToAbsolutePos,
8
- extractHTMLElementsIndexes,
9
- extractJSONValueIndexes,
10
- getRedactionsForChunkHeaders,
11
- makeRegex,
12
- matchRedactedStrings,
13
- parseHttpResponse
14
- } from "../../providers/http/utils.js";
5
+ import { buildHeaders, convertResponsePosToAbsolutePos, extractHTMLElementsIndexes, extractJSONValueIndexes, getRedactionsForChunkHeaders, makeRegex, matchRedactedStrings, parseHttpResponse, } from "./utils.js";
15
6
  import { isValidProxySessionId } from "../../server/utils/proxy-session.js";
16
- import {
17
- findIndexInUint8Array,
18
- getHttpRequestDataFromTranscript,
19
- logger,
20
- REDACTION_CHAR_CODE,
21
- strToUint8Array,
22
- uint8ArrayToStr
23
- } from "../../utils/index.js";
24
- const OK_HTTP_HEADER = "HTTP/1.1 200";
25
- const dateHeaderRegex = "[dD]ate: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (?:[0-3][0-9]) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (?:[0-9]{4}) (?:[01][0-9]|2[0-3])(?::[0-5][0-9]){2} GMT)";
26
- const dateDiff = 1e3 * 60 * 10;
7
+ import { findIndexInUint8Array, getHttpRequestDataFromTranscript, logger, REDACTION_CHAR_CODE, strToUint8Array, uint8ArrayToStr, } from "../../utils/index.js";
8
+ const OK_HTTP_HEADER = 'HTTP/1.1 200';
9
+ const dateHeaderRegex = '[dD]ate: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (?:[0-3][0-9]) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (?:[0-9]{4}) (?:[01][0-9]|2[0-3])(?::[0-5][0-9]){2} GMT)';
10
+ const dateDiff = 1000 * 60 * 10; // allow 10 min difference
27
11
  const HTTP_PROVIDER = {
28
- hostPort: getHostPort,
29
- writeRedactionMode(params) {
30
- return "writeRedactionMode" in params ? params.writeRedactionMode : void 0;
31
- },
32
- geoLocation(params, secretParams) {
33
- return "geoLocation" in params ? getGeoLocation(params, secretParams) : void 0;
34
- },
35
- proxySessionId(params, secretParams) {
36
- return "proxySessionId" in params ? getProxySessionId(params, secretParams) : void 0;
37
- },
38
- additionalClientOptions(params) {
39
- let defaultOptions = {
40
- applicationLayerProtocols: ["http/1.1"]
41
- };
42
- if ("additionalClientOptions" in params) {
43
- defaultOptions = {
44
- ...defaultOptions,
45
- ...params.additionalClientOptions
46
- };
47
- }
48
- return defaultOptions;
49
- },
50
- createRequest(secretParams, params, logger2) {
51
- if (!secretParams.cookieStr && !secretParams.authorisationHeader && !secretParams.headers) {
52
- throw new Error("auth parameters are not set");
53
- }
54
- const pubHeaders = params.headers || {};
55
- const secHeaders = { ...secretParams.headers };
56
- if (secretParams.cookieStr) {
57
- secHeaders["Cookie"] = secretParams.cookieStr;
58
- }
59
- if (secretParams.authorisationHeader) {
60
- secHeaders["Authorization"] = secretParams.authorisationHeader;
61
- }
62
- const hasUserAgent = Object.keys(pubHeaders).some((k) => k.toLowerCase() === "user-agent") || Object.keys(secHeaders).some((k) => k.toLowerCase() === "user-agent");
63
- if (!hasUserAgent) {
64
- pubHeaders["User-Agent"] = RECLAIM_USER_AGENT;
65
- }
66
- const newParams = substituteParamValues(params, secretParams);
67
- params = newParams.newParams;
68
- const url = new URL(params.url);
69
- const { pathname } = url;
70
- const searchParams = params.url.includes("?") ? params.url.split("?")[1] : "";
71
- logger2.info({ url: params.url, path: pathname, query: searchParams.toString() });
72
- const body = params.body instanceof Uint8Array ? params.body : strToUint8Array(params.body || "");
73
- const contentLength = body.length;
74
- const reqLine = `${params.method} ${pathname}${searchParams?.length ? "?" + searchParams : ""} HTTP/1.1`;
75
- const secHeadersList = buildHeaders(secHeaders);
76
- logger2.info({ requestLine: reqLine });
77
- const httpReqHeaderStr = [
78
- reqLine,
79
- `Host: ${getHostHeaderString(url)}`,
80
- `Content-Length: ${contentLength}`,
81
- "Connection: close",
82
- //no compression
83
- "Accept-Encoding: identity",
84
- ...buildHeaders(pubHeaders),
85
- ...secHeadersList,
86
- "\r\n"
87
- ].join("\r\n");
88
- const headerStr = strToUint8Array(httpReqHeaderStr);
89
- const data = concatenateUint8Arrays([headerStr, body]);
90
- const secHeadersStr = secHeadersList.join("\r\n");
91
- const tokenStartIndex = findIndexInUint8Array(data, strToUint8Array(secHeadersStr));
92
- const redactions = [
93
- {
94
- fromIndex: tokenStartIndex,
95
- toIndex: tokenStartIndex + secHeadersStr.length
96
- }
97
- ];
98
- if (newParams.hiddenBodyParts?.length > 0) {
99
- for (const hiddenBodyPart of newParams.hiddenBodyParts) {
100
- if (hiddenBodyPart.length) {
101
- redactions.push({
102
- fromIndex: headerStr.length + hiddenBodyPart.index,
103
- toIndex: headerStr.length + hiddenBodyPart.index + hiddenBodyPart.length
104
- });
105
- }
106
- }
107
- }
108
- if (newParams.hiddenURLParts?.length > 0) {
109
- for (const hiddenURLPart of newParams.hiddenURLParts) {
110
- if (hiddenURLPart.length) {
111
- redactions.push({
112
- fromIndex: hiddenURLPart.index,
113
- toIndex: hiddenURLPart.index + hiddenURLPart.length
114
- });
115
- }
116
- }
117
- }
118
- redactions.sort((a, b) => a.toIndex - b.toIndex);
119
- return {
120
- data,
121
- redactions
122
- };
123
- },
124
- getResponseRedactions({ response, params: rawParams, logger: logger2, ctx }) {
125
- logger2.debug({ response: encodeBase64(response), params: rawParams });
126
- const res = parseHttpResponse(response);
127
- if (!rawParams.responseRedactions?.length) {
128
- return [];
129
- }
130
- if (res.statusCode / 100 >> 0 !== 2) {
131
- logger2.error({ response: encodeBase64(response), params: rawParams });
132
- throw new Error(
133
- `Expected status 2xx, got ${res.statusCode} (${res.statusMessage})`
134
- );
135
- }
136
- const newParams = substituteParamValues(rawParams, void 0, true);
137
- const params = newParams.newParams;
138
- const headerEndIndex = res.statusLineEndIndex;
139
- const bodyStartIdx = res.bodyStartIndex ?? 0;
140
- if (bodyStartIdx < 4) {
141
- logger2.error({ response: encodeBase64(response) });
142
- throw new Error("Failed to find response body");
143
- }
144
- const reveals = [
145
- { fromIndex: 0, toIndex: headerEndIndex }
146
- ];
147
- if (shouldRevealCrlf(ctx)) {
148
- const crlfs = response.slice(res.headerEndIdx, res.headerEndIdx + 4);
149
- if (!areUint8ArraysEqual(crlfs, strToUint8Array("\r\n\r\n"))) {
150
- logger2.error({ response: encodeBase64(response) });
151
- throw new Error(
152
- `Failed to find header/body separator at index ${res.headerEndIdx}`
153
- );
154
- }
155
- }
156
- reveals.push({ fromIndex: res.headerEndIdx, toIndex: res.headerEndIdx + 4 });
157
- if (res.headerIndices["date"]) {
158
- reveals.push(res.headerIndices["date"]);
159
- }
160
- const body = uint8ArrayToBinaryStr(res.body);
161
- const redactions = [];
162
- for (const rs of params.responseRedactions || []) {
163
- const processor = processRedactionRequest(
164
- body,
165
- rs,
166
- bodyStartIdx,
167
- res.chunks
168
- );
169
- for (const { reveal, redactions: reds } of processor) {
170
- reveals.push(reveal);
171
- redactions.push(...reds);
172
- }
173
- }
174
- reveals.sort((a, b) => a.toIndex - b.toIndex);
175
- if (reveals.length > 1) {
176
- let currentIndex = 0;
177
- for (const r of reveals) {
178
- if (currentIndex < r.fromIndex) {
179
- redactions.push({ fromIndex: currentIndex, toIndex: r.fromIndex });
180
- }
181
- currentIndex = r.toIndex;
182
- }
183
- redactions.push({ fromIndex: currentIndex, toIndex: response.length });
184
- }
185
- for (const r of reveals) {
186
- if (!r.hash) {
187
- continue;
188
- }
189
- redactions.push(r);
190
- }
191
- redactions.sort((a, b) => a.toIndex - b.toIndex);
192
- return redactions;
193
- },
194
- assertValidProviderReceipt({ receipt, params: paramsAny, logger: logger2, ctx }) {
195
- logTranscript();
196
- let extractedParams = {};
197
- const secretParams = "secretParams" in paramsAny ? paramsAny.secretParams : void 0;
198
- const newParams = substituteParamValues(paramsAny, secretParams, !secretParams);
199
- const params = newParams.newParams;
200
- extractedParams = { ...extractedParams, ...newParams.extractedValues };
201
- const req = getHttpRequestDataFromTranscript(receipt);
202
- if (req.method !== params.method.toLowerCase()) {
203
- throw new Error(`Invalid method: ${req.method}`);
204
- }
205
- const url = new URL(params.url);
206
- const { protocol, pathname } = url;
207
- if (protocol !== "https:") {
208
- logger2.error("params URL: %s", params.url);
209
- throw new Error(`Expected protocol: https, found: ${protocol}`);
210
- }
211
- const searchParams = params.url.includes("?") ? params.url.split("?")[1] : "";
212
- const expectedPath = pathname.replaceAll("%7B", "{").replaceAll("%7D", "}") + (searchParams?.length ? "?" + searchParams : "");
213
- if (!matchRedactedStrings(strToUint8Array(expectedPath), strToUint8Array(req.url))) {
214
- logger2.error("params URL: %s", params.url);
215
- throw new Error(`Expected path: ${expectedPath}, found: ${req.url}`);
216
- }
217
- const expectedHostStr = getHostHeaderString(url);
218
- if (req.headers.host !== expectedHostStr) {
219
- throw new Error(`Expected host: ${expectedHostStr}, found: ${req.headers.host}`);
220
- }
221
- const connectionHeader = req.headers["connection"];
222
- if (connectionHeader !== "close") {
223
- throw new Error(`Connection header must be "close", got "${connectionHeader}"`);
224
- }
225
- const serverBlocks = receipt.filter((s) => s.sender === "server").map((r) => r.message).filter((b) => !b.every((b2) => b2 === REDACTION_CHAR_CODE));
226
- const response = concatArrays(...serverBlocks);
227
- let res;
228
- res = uint8ArrayToStr(response);
229
- const okRegex = makeRegex("^HTTP\\/1.1 2\\d{2}");
230
- const matchRes = okRegex.exec(res);
231
- if (!matchRes) {
232
- const statusRegex = makeRegex("^HTTP\\/1.1 (\\d{3})");
233
- const matchRes2 = statusRegex.exec(res);
234
- if (matchRes2 && matchRes2.length > 1) {
235
- throw new Error(`Provider returned error ${matchRes2[1]}`);
236
- }
237
- let lineEnd = res.indexOf("*");
238
- if (lineEnd === -1) {
239
- lineEnd = res.indexOf("\n");
240
- }
241
- if (lineEnd === -1) {
242
- lineEnd = OK_HTTP_HEADER.length;
243
- }
244
- throw new Error(
245
- `Response did not start with "HTTP/1.1 2XX" got "${res.slice(0, lineEnd)}"`
246
- );
247
- }
248
- let bodyStart;
249
- if (shouldRevealCrlf(ctx)) {
250
- bodyStart = res.indexOf("\r\n\r\n", OK_HTTP_HEADER.length) + 4;
251
- if (bodyStart < 4) {
252
- throw new Error("Response body start not found");
253
- }
254
- } else {
255
- bodyStart = OK_HTTP_HEADER.length;
256
- }
257
- const dateHeader = makeRegex(dateHeaderRegex).exec(res);
258
- if (dateHeader && dateHeader.length > 1) {
259
- const serverDate = new Date(dateHeader[1]);
260
- if (Date.now() - serverDate.getTime() > dateDiff) {
261
- logger2.info({ dateHeader: dateHeader[0], current: Date.now() }, "date header is off");
262
- }
263
- }
264
- const paramBody = params.body instanceof Uint8Array ? params.body : strToUint8Array(params.body || "");
265
- if (paramBody.length > 0 && !matchRedactedStrings(paramBody, req.body)) {
266
- throw new Error("request body mismatch");
267
- }
268
- if (!secretParams) {
269
- res = res.slice(bodyStart).replace(/(\*){3,}/g, "");
270
- }
271
- for (const { type, value, invert } of params.responseMatches || []) {
272
- const inv = Boolean(invert);
273
- switch (type) {
274
- case "regex":
275
- const regexRes = makeRegex(value).exec(res);
276
- const match = regexRes !== null;
277
- if (match === inv) {
278
- throw new Error(
279
- `Invalid receipt. Regex "${value}" ${invert ? "matched" : "didn't match"}`
280
- );
281
- }
282
- if (!match) {
283
- continue;
284
- }
285
- const groups = regexRes?.groups;
286
- for (const paramName in groups || []) {
287
- if (paramName in extractedParams) {
288
- throw new Error(`Duplicate parameter ${paramName}`);
289
- }
290
- const value2 = groups?.[paramName];
291
- if (typeof value2 !== "string") {
292
- continue;
293
- }
294
- extractedParams[paramName] = value2;
295
- }
296
- break;
297
- case "contains":
298
- const includes = res.includes(value);
299
- if (includes === inv) {
300
- throw new Error(
301
- `Invalid receipt. Response ${invert ? "contains" : "does not contain"} "${value}"`
302
- );
303
- }
304
- break;
305
- default:
306
- throw new Error(`Invalid response match type ${type}`);
307
- }
308
- }
309
- function concatArrays(...bufs) {
310
- const totalSize = bufs.reduce((acc, e) => acc + e.length, 0);
311
- const merged = new Uint8Array(totalSize);
312
- let lenDone = 0;
313
- for (const array of bufs) {
314
- merged.set(array, lenDone);
315
- lenDone += array.length;
316
- }
317
- return merged;
318
- }
319
- return { extractedParameters: extractedParams };
320
- function logTranscript() {
321
- const clientMsgs = receipt.filter((s) => s.sender === "client").map((m) => m.message);
322
- const serverMsgs = receipt.filter((s) => s.sender === "server").map((m) => m.message);
323
- const clientTranscript = encodeBase64(concatenateUint8Arrays(clientMsgs));
324
- const serverTranscript = encodeBase64(concatenateUint8Arrays(serverMsgs));
325
- logger2.debug({ request: clientTranscript, response: serverTranscript, params: paramsAny });
326
- }
327
- }
12
+ hostPort: getHostPort,
13
+ writeRedactionMode(params) {
14
+ return ('writeRedactionMode' in params)
15
+ ? params.writeRedactionMode
16
+ : undefined;
17
+ },
18
+ geoLocation(params, secretParams) {
19
+ return ('geoLocation' in params)
20
+ ? getGeoLocation(params, secretParams)
21
+ : undefined;
22
+ },
23
+ proxySessionId(params, secretParams) {
24
+ return ('proxySessionId' in params)
25
+ ? getProxySessionId(params, secretParams)
26
+ : undefined;
27
+ },
28
+ additionalClientOptions(params) {
29
+ let defaultOptions = {
30
+ applicationLayerProtocols: ['http/1.1']
31
+ };
32
+ if ('additionalClientOptions' in params) {
33
+ defaultOptions = {
34
+ ...defaultOptions,
35
+ ...params.additionalClientOptions
36
+ };
37
+ }
38
+ return defaultOptions;
39
+ },
40
+ createRequest(secretParams, params, logger) {
41
+ if (!secretParams.cookieStr &&
42
+ !secretParams.authorisationHeader &&
43
+ !secretParams.headers) {
44
+ throw new Error('auth parameters are not set');
45
+ }
46
+ const pubHeaders = params.headers || {};
47
+ const secHeaders = { ...secretParams.headers };
48
+ if (secretParams.cookieStr) {
49
+ secHeaders['Cookie'] = secretParams.cookieStr;
50
+ }
51
+ if (secretParams.authorisationHeader) {
52
+ secHeaders['Authorization'] = secretParams.authorisationHeader;
53
+ }
54
+ const hasUserAgent = Object.keys(pubHeaders)
55
+ .some(k => k.toLowerCase() === 'user-agent') ||
56
+ Object.keys(secHeaders)
57
+ .some(k => k.toLowerCase() === 'user-agent');
58
+ if (!hasUserAgent) {
59
+ //only set user-agent if not set by provider
60
+ pubHeaders['User-Agent'] = RECLAIM_USER_AGENT;
61
+ }
62
+ const newParams = substituteParamValues(params, secretParams);
63
+ params = newParams.newParams;
64
+ const url = new URL(params.url);
65
+ const { pathname } = url;
66
+ const searchParams = params.url.includes('?') ? params.url.split('?')[1] : '';
67
+ logger.info({ url: params.url, path: pathname, query: searchParams.toString() });
68
+ const body = params.body instanceof Uint8Array
69
+ ? params.body
70
+ : strToUint8Array(params.body || '');
71
+ const contentLength = body.length;
72
+ const reqLine = `${params.method} ${pathname}${searchParams?.length ? '?' + searchParams : ''} HTTP/1.1`;
73
+ const secHeadersList = buildHeaders(secHeaders);
74
+ logger.info({ requestLine: reqLine });
75
+ const httpReqHeaderStr = [
76
+ reqLine,
77
+ `Host: ${getHostHeaderString(url)}`,
78
+ `Content-Length: ${contentLength}`,
79
+ 'Connection: close',
80
+ //no compression
81
+ 'Accept-Encoding: identity',
82
+ ...buildHeaders(pubHeaders),
83
+ ...secHeadersList,
84
+ '\r\n',
85
+ ].join('\r\n');
86
+ const headerStr = strToUint8Array(httpReqHeaderStr);
87
+ const data = concatenateUint8Arrays([headerStr, body]);
88
+ // hide all secret headers
89
+ const secHeadersStr = secHeadersList.join('\r\n');
90
+ const tokenStartIndex = findIndexInUint8Array(data, strToUint8Array(secHeadersStr));
91
+ const redactions = [
92
+ {
93
+ fromIndex: tokenStartIndex,
94
+ toIndex: tokenStartIndex + secHeadersStr.length,
95
+ }
96
+ ];
97
+ if (newParams.hiddenBodyParts?.length > 0) {
98
+ for (const hiddenBodyPart of newParams.hiddenBodyParts) {
99
+ if (hiddenBodyPart.length) {
100
+ redactions.push({
101
+ fromIndex: headerStr.length + hiddenBodyPart.index,
102
+ toIndex: headerStr.length + hiddenBodyPart.index + hiddenBodyPart.length,
103
+ });
104
+ }
105
+ }
106
+ }
107
+ if (newParams.hiddenURLParts?.length > 0) {
108
+ for (const hiddenURLPart of newParams.hiddenURLParts) {
109
+ if (hiddenURLPart.length) {
110
+ redactions.push({
111
+ fromIndex: hiddenURLPart.index,
112
+ toIndex: hiddenURLPart.index + hiddenURLPart.length,
113
+ });
114
+ }
115
+ }
116
+ }
117
+ redactions.sort((a, b) => a.toIndex - b.toIndex);
118
+ return {
119
+ data,
120
+ redactions: redactions,
121
+ };
122
+ },
123
+ getResponseRedactions({ response, params: rawParams, logger, ctx }) {
124
+ logger.debug({ response: encodeBase64(response), params: rawParams });
125
+ const res = parseHttpResponse(response);
126
+ if (!rawParams.responseRedactions?.length) {
127
+ return [];
128
+ }
129
+ if (((res.statusCode / 100) >> 0) !== 2) {
130
+ logger.error({ response: encodeBase64(response), params: rawParams });
131
+ throw new Error(`Expected status 2xx, got ${res.statusCode} (${res.statusMessage})`);
132
+ }
133
+ const newParams = substituteParamValues(rawParams, undefined, true);
134
+ const params = newParams.newParams;
135
+ const headerEndIndex = res.statusLineEndIndex;
136
+ const bodyStartIdx = res.bodyStartIndex ?? 0;
137
+ if (bodyStartIdx < 4) {
138
+ logger.error({ response: encodeBase64(response) });
139
+ throw new Error('Failed to find response body');
140
+ }
141
+ const reveals = [
142
+ { fromIndex: 0, toIndex: headerEndIndex }
143
+ ];
144
+ //reveal double CRLF which separates headers from body
145
+ if (shouldRevealCrlf(ctx)) {
146
+ const crlfs = response
147
+ .slice(res.headerEndIdx, res.headerEndIdx + 4);
148
+ if (!areUint8ArraysEqual(crlfs, strToUint8Array('\r\n\r\n'))) {
149
+ logger.error({ response: encodeBase64(response) });
150
+ throw new Error(`Failed to find header/body separator at index ${res.headerEndIdx}`);
151
+ }
152
+ }
153
+ reveals.push({ fromIndex: res.headerEndIdx, toIndex: res.headerEndIdx + 4 });
154
+ //reveal date header
155
+ if (res.headerIndices['date']) {
156
+ reveals.push(res.headerIndices['date']);
157
+ }
158
+ const body = uint8ArrayToBinaryStr(res.body);
159
+ const redactions = [];
160
+ for (const rs of params.responseRedactions || []) {
161
+ const processor = processRedactionRequest(body, rs, bodyStartIdx, res.chunks);
162
+ for (const { reveal, redactions: reds } of processor) {
163
+ reveals.push(reveal);
164
+ redactions.push(...reds);
165
+ }
166
+ }
167
+ reveals.sort((a, b) => a.toIndex - b.toIndex);
168
+ if (reveals.length > 1) {
169
+ let currentIndex = 0;
170
+ for (const r of reveals) {
171
+ if (currentIndex < r.fromIndex) {
172
+ redactions.push({ fromIndex: currentIndex, toIndex: r.fromIndex });
173
+ }
174
+ currentIndex = r.toIndex;
175
+ }
176
+ redactions.push({ fromIndex: currentIndex, toIndex: response.length });
177
+ }
178
+ for (const r of reveals) {
179
+ if (!r.hash) {
180
+ continue;
181
+ }
182
+ redactions.push(r);
183
+ }
184
+ redactions.sort((a, b) => a.toIndex - b.toIndex);
185
+ return redactions;
186
+ },
187
+ assertValidProviderReceipt({ receipt, params: paramsAny, logger, ctx }) {
188
+ logTranscript();
189
+ let extractedParams = {};
190
+ const secretParams = ('secretParams' in paramsAny)
191
+ ? paramsAny.secretParams
192
+ : undefined;
193
+ const newParams = substituteParamValues(paramsAny, secretParams, !secretParams);
194
+ const params = newParams.newParams;
195
+ extractedParams = { ...extractedParams, ...newParams.extractedValues };
196
+ const req = getHttpRequestDataFromTranscript(receipt);
197
+ if (req.method !== params.method.toLowerCase()) {
198
+ throw new Error(`Invalid method: ${req.method}`);
199
+ }
200
+ const url = new URL(params.url);
201
+ const { protocol, pathname } = url;
202
+ if (protocol !== 'https:') {
203
+ logger.error('params URL: %s', params.url);
204
+ throw new Error(`Expected protocol: https, found: ${protocol}`);
205
+ }
206
+ const searchParams = params.url.includes('?') ? params.url.split('?')[1] : '';
207
+ //brackets in URL path turn into %7B and %7D, so replace them back
208
+ const expectedPath = pathname.replaceAll('%7B', '{').replaceAll('%7D', '}') + (searchParams?.length ? '?' + searchParams : '');
209
+ if (!matchRedactedStrings(strToUint8Array(expectedPath), strToUint8Array(req.url))) {
210
+ logger.error('params URL: %s', params.url);
211
+ throw new Error(`Expected path: ${expectedPath}, found: ${req.url}`);
212
+ }
213
+ const expectedHostStr = getHostHeaderString(url);
214
+ if (req.headers.host !== expectedHostStr) {
215
+ throw new Error(`Expected host: ${expectedHostStr}, found: ${req.headers.host}`);
216
+ }
217
+ const connectionHeader = req.headers['connection'];
218
+ if (connectionHeader !== 'close') {
219
+ throw new Error(`Connection header must be "close", got "${connectionHeader}"`);
220
+ }
221
+ const serverBlocks = receipt
222
+ .filter(s => s.sender === 'server')
223
+ .map((r) => r.message)
224
+ // filter out fully redacted blocks
225
+ .filter(b => !b.every(b => b === REDACTION_CHAR_CODE));
226
+ const response = concatArrays(...serverBlocks);
227
+ let res;
228
+ res = uint8ArrayToStr(response);
229
+ const okRegex = makeRegex('^HTTP\\/1.1 2\\d{2}');
230
+ const matchRes = okRegex.exec(res);
231
+ if (!matchRes) {
232
+ const statusRegex = makeRegex('^HTTP\\/1.1 (\\d{3})');
233
+ const matchRes = statusRegex.exec(res);
234
+ if (matchRes && matchRes.length > 1) {
235
+ throw new Error(`Provider returned error ${matchRes[1]}`);
236
+ }
237
+ let lineEnd = res.indexOf('*');
238
+ if (lineEnd === -1) {
239
+ lineEnd = res.indexOf('\n');
240
+ }
241
+ if (lineEnd === -1) {
242
+ lineEnd = OK_HTTP_HEADER.length;
243
+ }
244
+ throw new Error(`Response did not start with \"HTTP/1.1 2XX\" got "${res.slice(0, lineEnd)}"`);
245
+ }
246
+ let bodyStart;
247
+ if (shouldRevealCrlf(ctx)) {
248
+ bodyStart = res.indexOf('\r\n\r\n', OK_HTTP_HEADER.length) + 4;
249
+ if (bodyStart < 4) {
250
+ throw new Error('Response body start not found');
251
+ }
252
+ }
253
+ else {
254
+ bodyStart = OK_HTTP_HEADER.length;
255
+ }
256
+ //validate server Date header if present
257
+ const dateHeader = makeRegex(dateHeaderRegex).exec(res);
258
+ if (dateHeader && dateHeader.length > 1) {
259
+ const serverDate = new Date(dateHeader[1]);
260
+ if ((Date.now() - serverDate.getTime()) > dateDiff) {
261
+ logger.info({ dateHeader: dateHeader[0], current: Date.now() }, 'date header is off');
262
+ // too many false positives
263
+ // throw new Error(
264
+ // `Server date is off by "${(Date.now() - serverDate.getTime()) / 1000} s"`
265
+ // )
266
+ }
267
+ }
268
+ const paramBody = params.body instanceof Uint8Array
269
+ ? params.body
270
+ : strToUint8Array(params.body || '');
271
+ if (paramBody.length > 0 && !matchRedactedStrings(paramBody, req.body)) {
272
+ throw new Error('request body mismatch');
273
+ }
274
+ //remove asterisks to account for chunks in the middle of revealed strings
275
+ if (!secretParams) {
276
+ res = res.slice(bodyStart).replace(/(\*){3,}/g, '');
277
+ }
278
+ for (const { type, value, invert } of params.responseMatches || []) {
279
+ const inv = Boolean(invert); // explicitly cast to boolean
280
+ switch (type) {
281
+ case 'regex':
282
+ const regexRes = makeRegex(value).exec(res);
283
+ const match = regexRes !== null;
284
+ if (match === inv) { // if both true or both false then fail
285
+ throw new Error('Invalid receipt.'
286
+ + ` Regex "${value}" ${invert ? 'matched' : 'didn\'t match'}`);
287
+ }
288
+ if (!match) {
289
+ continue;
290
+ }
291
+ const groups = regexRes?.groups;
292
+ for (const paramName in groups || []) {
293
+ if (paramName in extractedParams) {
294
+ throw new Error(`Duplicate parameter ${paramName}`);
295
+ }
296
+ const value = groups?.[paramName];
297
+ if (typeof value !== 'string') {
298
+ continue;
299
+ }
300
+ extractedParams[paramName] = value;
301
+ }
302
+ break;
303
+ case 'contains':
304
+ const includes = res.includes(value);
305
+ if (includes === inv) {
306
+ throw new Error(`Invalid receipt. Response ${invert ? 'contains' : 'does not contain'} "${value}"`);
307
+ }
308
+ break;
309
+ default:
310
+ throw new Error(`Invalid response match type ${type}`);
311
+ }
312
+ }
313
+ function concatArrays(...bufs) {
314
+ const totalSize = bufs.reduce((acc, e) => acc + e.length, 0);
315
+ const merged = new Uint8Array(totalSize);
316
+ let lenDone = 0;
317
+ for (const array of bufs) {
318
+ merged.set(array, lenDone);
319
+ lenDone += array.length;
320
+ }
321
+ return merged;
322
+ }
323
+ return { extractedParameters: extractedParams };
324
+ function logTranscript() {
325
+ const clientMsgs = receipt.filter(s => s.sender === 'client').map(m => m.message);
326
+ const serverMsgs = receipt.filter(s => s.sender === 'server').map(m => m.message);
327
+ const clientTranscript = encodeBase64(concatenateUint8Arrays(clientMsgs));
328
+ const serverTranscript = encodeBase64(concatenateUint8Arrays(serverMsgs));
329
+ logger.debug({ request: clientTranscript, response: serverTranscript, params: paramsAny });
330
+ }
331
+ },
328
332
  };
333
+ // revealing CRLF is a breaking change -- and should only be done
334
+ // if the client's version supports it
329
335
  function shouldRevealCrlf({ version }) {
330
- return version >= AttestorVersion.ATTESTOR_VERSION_2_0_1;
336
+ return version >= AttestorVersion.ATTESTOR_VERSION_2_0_1;
331
337
  }
332
338
  function getHostPort(params, secretParams) {
333
- const { host } = new URL(getURL(params, secretParams));
334
- if (!host) {
335
- throw new Error("url is incorrect");
336
- }
337
- return host;
339
+ const { host } = new URL(getURL(params, secretParams));
340
+ if (!host) {
341
+ throw new Error('url is incorrect');
342
+ }
343
+ return host;
338
344
  }
345
+ /**
346
+ * Obtain the host header string from the URL.
347
+ * https://stackoverflow.com/a/3364396
348
+ */
339
349
  function getHostHeaderString(url) {
340
- const host = url.hostname;
341
- const port = url.port;
342
- return port && +port !== DEFAULT_HTTPS_PORT ? `${host}:${port}` : host;
350
+ const host = url.hostname;
351
+ const port = url.port;
352
+ return port && +port !== DEFAULT_HTTPS_PORT
353
+ ? `${host}:${port}`
354
+ : host;
343
355
  }
344
356
  const paramsRegex = /{{([^{}]+)}}/sgi;
345
357
  function* processRedactionRequest(body, rs, bodyStartIdx, resChunks) {
346
- let element = body;
347
- let elementIdx = 0;
348
- let elementLength = -1;
349
- if (rs.xPath) {
350
- const indexes = extractHTMLElementsIndexes(body, rs.xPath, !!rs.jsonPath);
351
- for (const { start, end } of indexes) {
352
- element = body.slice(start, end);
353
- elementIdx = start;
354
- elementLength = end - start;
355
- if (rs.jsonPath) {
358
+ let element = body;
359
+ let elementIdx = 0;
360
+ let elementLength = -1;
361
+ if (rs.xPath) {
362
+ const indexes = extractHTMLElementsIndexes(body, rs.xPath, !!rs.jsonPath);
363
+ for (const { start, end } of indexes) {
364
+ element = body.slice(start, end);
365
+ elementIdx = start;
366
+ elementLength = end - start;
367
+ if (rs.jsonPath) {
368
+ yield* processJsonPath();
369
+ }
370
+ else if (rs.regex) {
371
+ yield* processRegexp();
372
+ }
373
+ else {
374
+ yield* addRedaction();
375
+ }
376
+ }
377
+ }
378
+ else if (rs.jsonPath) {
356
379
  yield* processJsonPath();
357
- } else if (rs.regex) {
358
- yield* processRegexp();
359
- } else {
360
- yield* addRedaction();
361
- }
362
380
  }
363
- } else if (rs.jsonPath) {
364
- yield* processJsonPath();
365
- } else if (rs.regex) {
366
- yield* processRegexp();
367
- } else {
368
- throw new Error(
369
- "Expected either xPath, jsonPath or regex for redaction"
370
- );
371
- }
372
- function* processJsonPath() {
373
- const jsonPathIndexes = extractJSONValueIndexes(element, rs.jsonPath);
374
- const eIndex = elementIdx;
375
- for (const ji of jsonPathIndexes) {
376
- const jStart = ji.start;
377
- const jEnd = ji.end;
378
- element = body.slice(eIndex + jStart, eIndex + jEnd);
379
- elementIdx = eIndex + jStart;
380
- elementLength = jEnd - jStart;
381
- if (rs.regex) {
381
+ else if (rs.regex) {
382
382
  yield* processRegexp();
383
- } else {
384
- yield* addRedaction();
385
- }
386
- }
387
- }
388
- function* processRegexp() {
389
- logger.debug({
390
- element: encodeBase64(strToUint8Array(element)),
391
- body: encodeBase64(strToUint8Array(body))
392
- });
393
- const regexp = makeRegex(rs.regex);
394
- const elem = element || body;
395
- const match = regexp.exec(elem);
396
- if (!match?.[0]) {
397
- throw new Error(
398
- `regexp ${rs.regex} does not match found element '${encodeBase64(strToUint8Array(elem))}'`
399
- );
400
383
  }
401
- elementIdx += match.index;
402
- elementLength = regexp.lastIndex - match.index;
403
- element = match[0];
404
- if (rs.hash && (!match.groups || Object.keys(match.groups).length > 1)) {
405
- throw new Error(
406
- "Exactly one named capture group is needed per hashed redaction"
407
- );
408
- }
409
- if (!rs.hash || !match.groups) {
410
- yield* addRedaction();
411
- return;
384
+ else {
385
+ throw new Error('Expected either xPath, jsonPath or regex for redaction');
386
+ }
387
+ function* processJsonPath() {
388
+ const jsonPathIndexes = extractJSONValueIndexes(element, rs.jsonPath);
389
+ const eIndex = elementIdx;
390
+ for (const ji of jsonPathIndexes) {
391
+ const jStart = ji.start;
392
+ const jEnd = ji.end;
393
+ element = body.slice(eIndex + jStart, eIndex + jEnd);
394
+ elementIdx = eIndex + jStart;
395
+ elementLength = jEnd - jStart;
396
+ if (rs.regex) {
397
+ yield* processRegexp();
398
+ }
399
+ else {
400
+ yield* addRedaction();
401
+ }
402
+ }
412
403
  }
413
- const fullStr = match[0];
414
- const grp = Object.values(match.groups)[0];
415
- const grpIdx = fullStr.indexOf(grp);
416
- elementLength = grpIdx;
417
- element = fullStr.slice(0, grpIdx);
418
- yield* addRedaction(null);
419
- elementIdx += grpIdx;
420
- element = grp;
421
- elementLength = grp.length;
422
- const reveal = getReveal(elementIdx, elementLength, rs.hash);
423
- const chunkReds = getRedactionsForChunkHeaders(reveal.fromIndex, reveal.toIndex, resChunks);
424
- if (chunkReds.length) {
425
- throw new Error(
426
- "Hash redactions cannot be performed if the redacted string is split between 2 or more HTTP chunks"
427
- );
404
+ function* processRegexp() {
405
+ logger.debug({
406
+ element: encodeBase64(strToUint8Array(element)),
407
+ body: encodeBase64(strToUint8Array(body))
408
+ });
409
+ const regexp = makeRegex(rs.regex);
410
+ const elem = element || body;
411
+ const match = regexp.exec(elem);
412
+ if (!match?.[0]) {
413
+ throw new Error(`regexp ${rs.regex} does not match found element '${encodeBase64(strToUint8Array(elem))}'`);
414
+ }
415
+ elementIdx += match.index;
416
+ elementLength = regexp.lastIndex - match.index;
417
+ element = match[0];
418
+ if (rs.hash && (!match.groups || Object.keys(match.groups).length > 1)) {
419
+ throw new Error('Exactly one named capture group is needed per hashed redaction');
420
+ }
421
+ // if there are groups in the regex,
422
+ // we'll only hash the group values
423
+ if (!rs.hash || !match.groups) {
424
+ yield* addRedaction();
425
+ return;
426
+ }
427
+ const fullStr = match[0];
428
+ const grp = Object.values(match.groups)[0];
429
+ const grpIdx = fullStr.indexOf(grp);
430
+ // don't hash the entire regex, we'll hash the group values
431
+ elementLength = grpIdx;
432
+ element = fullStr.slice(0, grpIdx);
433
+ yield* addRedaction(null);
434
+ elementIdx += grpIdx;
435
+ element = grp;
436
+ elementLength = grp.length;
437
+ const reveal = getReveal(elementIdx, elementLength, rs.hash);
438
+ const chunkReds = getRedactionsForChunkHeaders(reveal.fromIndex, reveal.toIndex, resChunks);
439
+ if (chunkReds.length) {
440
+ throw new Error('Hash redactions cannot be performed if '
441
+ + 'the redacted string is split between 2'
442
+ + ' or more HTTP chunks');
443
+ }
444
+ yield { reveal, redactions: chunkReds };
445
+ elementIdx += grp.length;
446
+ element = fullStr.slice(grpIdx + grp.length);
447
+ elementLength = element.length;
448
+ yield* addRedaction(null);
449
+ }
450
+ function* addRedaction(hash = rs.hash, _resChunks = resChunks) {
451
+ if (elementIdx < 0 || !elementLength) {
452
+ return;
453
+ }
454
+ const reveal = getReveal(elementIdx, elementLength, hash || undefined);
455
+ yield {
456
+ reveal,
457
+ redactions: getRedactionsForChunkHeaders(reveal.fromIndex, reveal.toIndex, _resChunks)
458
+ };
428
459
  }
429
- yield { reveal, redactions: chunkReds };
430
- elementIdx += grp.length;
431
- element = fullStr.slice(grpIdx + grp.length);
432
- elementLength = element.length;
433
- yield* addRedaction(null);
434
- }
435
- function* addRedaction(hash = rs.hash, _resChunks = resChunks) {
436
- if (elementIdx < 0 || !elementLength) {
437
- return;
460
+ function getReveal(startIdx, len, hash) {
461
+ const from = convertResponsePosToAbsolutePos(startIdx, bodyStartIdx, resChunks);
462
+ const to = convertResponsePosToAbsolutePos(startIdx + len, bodyStartIdx, resChunks);
463
+ return { fromIndex: from, toIndex: to, hash };
438
464
  }
439
- const reveal = getReveal(elementIdx, elementLength, hash || void 0);
440
- yield {
441
- reveal,
442
- redactions: getRedactionsForChunkHeaders(
443
- reveal.fromIndex,
444
- reveal.toIndex,
445
- _resChunks
446
- )
447
- };
448
- }
449
- function getReveal(startIdx, len, hash) {
450
- const from = convertResponsePosToAbsolutePos(
451
- startIdx,
452
- bodyStartIdx,
453
- resChunks
454
- );
455
- const to = convertResponsePosToAbsolutePos(
456
- startIdx + len,
457
- bodyStartIdx,
458
- resChunks
459
- );
460
- return { fromIndex: from, toIndex: to, hash };
461
- }
462
465
  }
463
- function substituteParamValues(currentParams, secretParams, ignoreMissingParams) {
464
- const params = JSON.parse(JSON.stringify(currentParams));
465
- let extractedValues = {};
466
- const hiddenURLParts = [];
467
- const urlParams = extractAndReplaceTemplateValues(params.url, ignoreMissingParams);
468
- if (urlParams) {
469
- params.url = urlParams.newParam;
470
- extractedValues = { ...urlParams.extractedValues };
471
- if (urlParams.hiddenParts.length) {
472
- const host = getHostHeaderString(new URL(params.url));
473
- const offset = `https://${host}`.length - currentParams.method.length - 1;
474
- for (const hiddenURLPart of urlParams.hiddenParts) {
475
- hiddenURLParts.push({ index: hiddenURLPart.index - offset, length: hiddenURLPart.length });
476
- }
477
- }
478
- }
479
- let bodyParams;
480
- let hiddenBodyParts = [];
481
- if (params.body) {
482
- const strBody = typeof params.body === "string" ? params.body : uint8ArrayToStr(params.body);
483
- bodyParams = extractAndReplaceTemplateValues(strBody, ignoreMissingParams);
484
- if (bodyParams) {
485
- params.body = bodyParams.newParam;
486
- extractedValues = { ...extractedValues, ...bodyParams.extractedValues };
487
- hiddenBodyParts = bodyParams.hiddenParts;
466
+ export function substituteParamValues(currentParams, secretParams, ignoreMissingParams) {
467
+ const params = JSON.parse(JSON.stringify(currentParams));
468
+ let extractedValues = {};
469
+ const hiddenURLParts = [];
470
+ const urlParams = extractAndReplaceTemplateValues(params.url, ignoreMissingParams);
471
+ if (urlParams) {
472
+ params.url = urlParams.newParam;
473
+ extractedValues = { ...urlParams.extractedValues };
474
+ if (urlParams.hiddenParts.length) {
475
+ const host = getHostHeaderString(new URL(params.url));
476
+ const offset = `https://${host}`.length - currentParams.method.length - 1; //space between method and start of the path
477
+ for (const hiddenURLPart of urlParams.hiddenParts) {
478
+ hiddenURLParts.push({ index: hiddenURLPart.index - offset, length: hiddenURLPart.length });
479
+ }
480
+ }
488
481
  }
489
- }
490
- const geoParams = extractAndReplaceTemplateValues(params.geoLocation);
491
- if (geoParams) {
492
- params.geoLocation = geoParams.newParam;
493
- extractedValues = { ...extractedValues, ...geoParams.extractedValues };
494
- }
495
- const proxySessionIdParams = extractAndReplaceTemplateValues(params.proxySessionId);
496
- if (proxySessionIdParams) {
497
- params.proxySessionId = proxySessionIdParams.newParam;
498
- extractedValues = { ...extractedValues, ...proxySessionIdParams.extractedValues };
499
- }
500
- if (params.responseRedactions) {
501
- for (const r of params.responseRedactions) {
502
- if (r.regex) {
503
- const regexParams = extractAndReplaceTemplateValues(r.regex);
504
- r.regex = regexParams?.newParam;
505
- }
506
- if (r.xPath) {
507
- const xpathParams = extractAndReplaceTemplateValues(r.xPath);
508
- r.xPath = xpathParams?.newParam;
509
- }
510
- if (r.jsonPath) {
511
- const jsonPathParams = extractAndReplaceTemplateValues(r.jsonPath);
512
- r.jsonPath = jsonPathParams?.newParam;
513
- }
482
+ let bodyParams;
483
+ let hiddenBodyParts = [];
484
+ if (params.body) {
485
+ const strBody = typeof params.body === 'string' ? params.body : uint8ArrayToStr(params.body);
486
+ bodyParams = extractAndReplaceTemplateValues(strBody, ignoreMissingParams);
487
+ if (bodyParams) {
488
+ params.body = bodyParams.newParam;
489
+ extractedValues = { ...extractedValues, ...bodyParams.extractedValues };
490
+ hiddenBodyParts = bodyParams.hiddenParts;
491
+ }
514
492
  }
515
- }
516
- if (params.responseMatches) {
517
- for (const r of params.responseMatches) {
518
- if (r.value !== "") {
519
- const matchParam = extractAndReplaceTemplateValues(r.value);
520
- r.value = matchParam?.newParam;
521
- extractedValues = { ...extractedValues, ...matchParam?.extractedValues };
522
- }
493
+ const geoParams = extractAndReplaceTemplateValues(params.geoLocation);
494
+ if (geoParams) {
495
+ params.geoLocation = geoParams.newParam;
496
+ extractedValues = { ...extractedValues, ...geoParams.extractedValues };
497
+ }
498
+ const proxySessionIdParams = extractAndReplaceTemplateValues(params.proxySessionId);
499
+ if (proxySessionIdParams) {
500
+ params.proxySessionId = proxySessionIdParams.newParam;
501
+ extractedValues = { ...extractedValues, ...proxySessionIdParams.extractedValues };
502
+ }
503
+ if (params.responseRedactions) {
504
+ for (const r of params.responseRedactions) {
505
+ if (r.regex) {
506
+ const regexParams = extractAndReplaceTemplateValues(r.regex);
507
+ r.regex = regexParams?.newParam;
508
+ }
509
+ if (r.xPath) {
510
+ const xpathParams = extractAndReplaceTemplateValues(r.xPath);
511
+ r.xPath = xpathParams?.newParam;
512
+ }
513
+ if (r.jsonPath) {
514
+ const jsonPathParams = extractAndReplaceTemplateValues(r.jsonPath);
515
+ r.jsonPath = jsonPathParams?.newParam;
516
+ }
517
+ }
523
518
  }
524
- }
525
- return {
526
- newParams: params,
527
- extractedValues,
528
- hiddenBodyParts,
529
- hiddenURLParts
530
- };
531
- function extractAndReplaceTemplateValues(param, ignoreMissingParams2) {
532
- if (!param) {
533
- return null;
519
+ if (params.responseMatches) {
520
+ for (const r of params.responseMatches) {
521
+ if (r.value !== '') {
522
+ const matchParam = extractAndReplaceTemplateValues(r.value);
523
+ r.value = matchParam?.newParam;
524
+ extractedValues = { ...extractedValues, ...matchParam?.extractedValues };
525
+ }
526
+ }
534
527
  }
535
- const extractedValues2 = {};
536
- const hiddenParts = [];
537
- let totalOffset = 0;
538
- param = param.replace(paramsRegex, (match, pn, offset) => {
539
- if (params.paramValues && pn in params.paramValues) {
540
- extractedValues2[pn] = params.paramValues[pn];
541
- totalOffset += params.paramValues[pn].length - match.length;
542
- return params.paramValues[pn];
543
- } else if (secretParams) {
544
- if (secretParams?.paramValues && pn in secretParams?.paramValues) {
545
- hiddenParts.push({
546
- index: offset + totalOffset,
547
- length: secretParams.paramValues[pn].length
548
- });
549
- totalOffset += secretParams.paramValues[pn].length - match.length;
550
- return secretParams.paramValues[pn];
551
- } else {
552
- throw new Error(`parameter's "${pn}" value not found in paramValues and secret parameter's paramValues`);
553
- }
554
- } else {
555
- if (!!!ignoreMissingParams2) {
556
- throw new Error(`parameter's "${pn}" value not found in paramValues`);
557
- } else {
558
- return match;
559
- }
560
- }
561
- });
562
528
  return {
563
- newParam: param,
564
- extractedValues: extractedValues2,
565
- hiddenParts
529
+ newParams: params,
530
+ extractedValues: extractedValues,
531
+ hiddenBodyParts: hiddenBodyParts,
532
+ hiddenURLParts: hiddenURLParts
566
533
  };
567
- }
534
+ function extractAndReplaceTemplateValues(param, ignoreMissingParams) {
535
+ if (!param) {
536
+ return null;
537
+ }
538
+ //const paramNames: Set<string> = new Set()
539
+ const extractedValues = {};
540
+ const hiddenParts = [];
541
+ let totalOffset = 0;
542
+ param = param.replace(paramsRegex, (match, pn, offset) => {
543
+ if (params.paramValues && pn in params.paramValues) {
544
+ extractedValues[pn] = params.paramValues[pn];
545
+ totalOffset += params.paramValues[pn].length - match.length;
546
+ return params.paramValues[pn];
547
+ }
548
+ else if (secretParams) {
549
+ if (secretParams?.paramValues && pn in secretParams?.paramValues) {
550
+ hiddenParts.push({
551
+ index: offset + totalOffset,
552
+ length: secretParams.paramValues[pn].length,
553
+ });
554
+ totalOffset += secretParams.paramValues[pn].length - match.length;
555
+ return secretParams.paramValues[pn];
556
+ }
557
+ else {
558
+ throw new Error(`parameter's "${pn}" value not found in paramValues and secret parameter's paramValues`);
559
+ }
560
+ }
561
+ else {
562
+ if (!(!!ignoreMissingParams)) {
563
+ throw new Error(`parameter's "${pn}" value not found in paramValues`);
564
+ }
565
+ else {
566
+ return match;
567
+ }
568
+ }
569
+ });
570
+ return {
571
+ newParam: param,
572
+ extractedValues: extractedValues,
573
+ hiddenParts: hiddenParts
574
+ };
575
+ }
568
576
  }
569
577
  function getGeoLocation(v2Params, secretParams) {
570
- if (v2Params?.geoLocation) {
571
- const paramNames = /* @__PURE__ */ new Set();
572
- let geo = v2Params.geoLocation;
573
- let match = null;
574
- while (match = paramsRegex.exec(geo)) {
575
- paramNames.add(match[1]);
576
- }
577
- for (const pn of paramNames) {
578
- if (v2Params.paramValues && pn in v2Params.paramValues) {
579
- geo = geo?.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString());
580
- } else if (secretParams?.paramValues && pn in secretParams.paramValues) {
581
- geo = geo?.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString());
582
- } else {
583
- throw new Error(`parameter "${pn}" value not found in templateParams`);
584
- }
585
- }
586
- const geoRegex = /^[A-Za-z]{2}$/sgiu;
587
- if (!geoRegex.test(geo)) {
588
- throw new Error(`Geolocation ${geo} is invalid`);
578
+ if (v2Params?.geoLocation) {
579
+ const paramNames = new Set();
580
+ let geo = v2Params.geoLocation;
581
+ //extract param names
582
+ let match = null;
583
+ while (match = paramsRegex.exec(geo)) {
584
+ paramNames.add(match[1]);
585
+ }
586
+ for (const pn of paramNames) {
587
+ if (v2Params.paramValues && pn in v2Params.paramValues) {
588
+ geo = geo?.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString());
589
+ }
590
+ else if (secretParams?.paramValues && pn in secretParams.paramValues) {
591
+ geo = geo?.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString());
592
+ }
593
+ else {
594
+ throw new Error(`parameter "${pn}" value not found in templateParams`);
595
+ }
596
+ }
597
+ const geoRegex = /^[A-Za-z]{2}$/sgiu;
598
+ if (!geoRegex.test(geo)) {
599
+ throw new Error(`Geolocation ${geo} is invalid`);
600
+ }
601
+ return geo;
589
602
  }
590
- return geo;
591
- }
592
- return void 0;
603
+ return undefined;
593
604
  }
594
605
  function getProxySessionId(v2Params, secretParams) {
595
- if (v2Params?.proxySessionId) {
596
- const paramNames = /* @__PURE__ */ new Set();
597
- let proxySessionIdValue = v2Params.proxySessionId;
598
- let match = null;
599
- while (match = paramsRegex.exec(proxySessionIdValue)) {
600
- paramNames.add(match[1]);
601
- }
602
- for (const pn of paramNames) {
603
- if (v2Params.paramValues && pn in v2Params.paramValues) {
604
- proxySessionIdValue = proxySessionIdValue?.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString());
605
- } else if (secretParams?.paramValues && pn in secretParams.paramValues) {
606
- proxySessionIdValue = proxySessionIdValue?.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString());
607
- } else {
608
- throw new Error(`parameter "${pn}" value not found in templateParams`);
609
- }
610
- }
611
- if (!isValidProxySessionId(proxySessionIdValue)) {
612
- throw new Error(`proxySessionId ${proxySessionIdValue} is invalid`);
606
+ if (v2Params?.proxySessionId) {
607
+ const paramNames = new Set();
608
+ let proxySessionIdValue = v2Params.proxySessionId;
609
+ //extract param names
610
+ let match = null;
611
+ while (match = paramsRegex.exec(proxySessionIdValue)) {
612
+ paramNames.add(match[1]);
613
+ }
614
+ for (const pn of paramNames) {
615
+ if (v2Params.paramValues && pn in v2Params.paramValues) {
616
+ proxySessionIdValue = proxySessionIdValue?.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString());
617
+ }
618
+ else if (secretParams?.paramValues && pn in secretParams.paramValues) {
619
+ proxySessionIdValue = proxySessionIdValue?.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString());
620
+ }
621
+ else {
622
+ throw new Error(`parameter "${pn}" value not found in templateParams`);
623
+ }
624
+ }
625
+ if (!isValidProxySessionId(proxySessionIdValue)) {
626
+ throw new Error(`proxySessionId ${proxySessionIdValue} is invalid`);
627
+ }
628
+ return proxySessionIdValue;
613
629
  }
614
- return proxySessionIdValue;
615
- }
616
- return void 0;
630
+ return undefined;
617
631
  }
618
632
  function getURL(v2Params, secretParams) {
619
- let hostPort = v2Params?.url;
620
- const paramNames = /* @__PURE__ */ new Set();
621
- let match = null;
622
- while (match = paramsRegex.exec(hostPort)) {
623
- paramNames.add(match[1]);
624
- }
625
- for (const pn of paramNames) {
626
- if (v2Params.paramValues && pn in v2Params.paramValues) {
627
- hostPort = hostPort?.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString());
628
- } else if (secretParams?.paramValues && pn in secretParams.paramValues) {
629
- hostPort = hostPort?.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString());
630
- } else {
631
- throw new Error(`parameter "${pn}" value not found in templateParams`);
633
+ let hostPort = v2Params?.url;
634
+ const paramNames = new Set();
635
+ //extract param names
636
+ let match = null;
637
+ while (match = paramsRegex.exec(hostPort)) {
638
+ paramNames.add(match[1]);
639
+ }
640
+ for (const pn of paramNames) {
641
+ if (v2Params.paramValues && pn in v2Params.paramValues) {
642
+ hostPort = hostPort?.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString());
643
+ }
644
+ else if (secretParams?.paramValues && pn in secretParams.paramValues) {
645
+ hostPort = hostPort?.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString());
646
+ }
647
+ else {
648
+ throw new Error(`parameter "${pn}" value not found in templateParams`);
649
+ }
632
650
  }
633
- }
634
- return hostPort;
651
+ return hostPort;
635
652
  }
636
- var http_default = HTTP_PROVIDER;
637
- export {
638
- http_default as default,
639
- substituteParamValues
640
- };
653
+ export default HTTP_PROVIDER;