UNPKG

@reclaimprotocol/attestor-core

Version:

<div> <div> <img src="https://raw.githubusercontent.com/reclaimprotocol/.github/main/assets/banners/Attestor-Core.png" /> </div> </div>

613 lines 60 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tls_1 = require("@reclaimprotocol/tls"); const utils_1 = require("ethers/lib/utils"); const config_1 = require("../../config"); const api_1 = require("../../proto/api"); const utils_2 = require("../../providers/http/utils"); const utils_3 = require("../../utils"); const OK_HTTP_HEADER = 'HTTP/1.1 200'; 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)'; const dateDiff = 1000 * 60 * 10; // allow 10 min difference const HTTP_PROVIDER = { hostPort: getHostPort, writeRedactionMode(params) { return ('writeRedactionMode' in params) ? params.writeRedactionMode : undefined; }, geoLocation(params, secretParams) { return ('geoLocation' in params) ? getGeoLocation(params, secretParams) : undefined; }, additionalClientOptions(params) { let defaultOptions = { applicationLayerProtocols: ['http/1.1'] }; if ('additionalClientOptions' in params) { defaultOptions = { ...defaultOptions, ...params.additionalClientOptions }; } return defaultOptions; }, createRequest(secretParams, params, logger) { var _a, _b; if (!secretParams.cookieStr && !secretParams.authorisationHeader && !secretParams.headers) { throw new Error('auth parameters are not set'); } const pubHeaders = params.headers || {}; const secHeaders = { ...secretParams.headers }; if (secretParams.cookieStr) { secHeaders['Cookie'] = secretParams.cookieStr; } if (secretParams.authorisationHeader) { secHeaders['Authorization'] = secretParams.authorisationHeader; } const hasUserAgent = Object.keys(pubHeaders) .some(k => k.toLowerCase() === 'user-agent') || Object.keys(secHeaders) .some(k => k.toLowerCase() === 'user-agent'); if (!hasUserAgent) { //only set user-agent if not set by provider pubHeaders['User-Agent'] = config_1.RECLAIM_USER_AGENT; } const newParams = substituteParamValues(params, secretParams); params = newParams.newParams; const url = new URL(params.url); const { pathname } = url; const searchParams = params.url.includes('?') ? params.url.split('?')[1] : ''; logger.info({ url: params.url, path: pathname, query: searchParams.toString() }); const body = params.body instanceof Uint8Array ? params.body : (0, tls_1.strToUint8Array)(params.body || ''); const contentLength = body.length; const reqLine = `${params.method} ${pathname}${(searchParams === null || searchParams === void 0 ? void 0 : searchParams.length) ? '?' + searchParams : ''} HTTP/1.1`; const secHeadersList = (0, utils_2.buildHeaders)(secHeaders); logger.info({ requestLine: reqLine }); const httpReqHeaderStr = [ reqLine, `Host: ${getHostHeaderString(url)}`, `Content-Length: ${contentLength}`, 'Connection: close', //no compression 'Accept-Encoding: identity', ...(0, utils_2.buildHeaders)(pubHeaders), ...secHeadersList, '\r\n', ].join('\r\n'); const headerStr = (0, tls_1.strToUint8Array)(httpReqHeaderStr); const data = (0, tls_1.concatenateUint8Arrays)([headerStr, body]); // hide all secret headers const secHeadersStr = secHeadersList.join('\r\n'); const tokenStartIndex = (0, utils_3.findIndexInUint8Array)(data, (0, tls_1.strToUint8Array)(secHeadersStr)); const redactions = [ { fromIndex: tokenStartIndex, toIndex: tokenStartIndex + secHeadersStr.length, } ]; if (((_a = newParams.hiddenBodyParts) === null || _a === void 0 ? void 0 : _a.length) > 0) { for (const hiddenBodyPart of newParams.hiddenBodyParts) { if (hiddenBodyPart.length) { redactions.push({ fromIndex: headerStr.length + hiddenBodyPart.index, toIndex: headerStr.length + hiddenBodyPart.index + hiddenBodyPart.length, }); } } } if (((_b = newParams.hiddenURLParts) === null || _b === void 0 ? void 0 : _b.length) > 0) { for (const hiddenURLPart of newParams.hiddenURLParts) { if (hiddenURLPart.length) { redactions.push({ fromIndex: hiddenURLPart.index, toIndex: hiddenURLPart.index + hiddenURLPart.length, }); } } } redactions.sort((a, b) => a.toIndex - b.toIndex); return { data, redactions: redactions, }; }, getResponseRedactions({ response, params: rawParams, logger, ctx }) { var _a, _b; logger.debug({ response: utils_1.base64.encode(response), params: rawParams }); const res = (0, utils_2.parseHttpResponse)(response); if (!((_a = rawParams.responseRedactions) === null || _a === void 0 ? void 0 : _a.length)) { return []; } if (((res.statusCode / 100) >> 0) !== 2) { logger.error({ response: utils_1.base64.encode(response), params: rawParams }); throw new Error(`Expected status 2xx, got ${res.statusCode} (${res.statusMessage})`); } const newParams = substituteParamValues(rawParams, undefined, true); const params = newParams.newParams; const headerEndIndex = res.statusLineEndIndex; const bodyStartIdx = (_b = res.bodyStartIndex) !== null && _b !== void 0 ? _b : 0; if (bodyStartIdx < 4) { logger.error({ response: (0, utils_3.uint8ArrayToBinaryStr)(response) }); throw new Error('Failed to find response body'); } const reveals = [ { fromIndex: 0, toIndex: headerEndIndex } ]; //reveal double CRLF which separates headers from body if (shouldRevealCrlf(ctx)) { const crlfs = response .slice(res.headerEndIdx, res.headerEndIdx + 4); if (!(0, tls_1.areUint8ArraysEqual)(crlfs, (0, tls_1.strToUint8Array)('\r\n\r\n'))) { logger.error({ response: (0, utils_3.uint8ArrayToBinaryStr)(response) }); throw new Error(`Failed to find header/body separator at index ${res.headerEndIdx}`); } } reveals.push({ fromIndex: res.headerEndIdx, toIndex: res.headerEndIdx + 4 }); //reveal date header if (res.headerIndices['date']) { reveals.push(res.headerIndices['date']); } const body = (0, utils_3.uint8ArrayToBinaryStr)(res.body); const redactions = []; for (const rs of params.responseRedactions || []) { const processor = processRedactionRequest(body, rs, bodyStartIdx, res.chunks); for (const { reveal, redactions: reds } of processor) { reveals.push(reveal); redactions.push(...reds); } } reveals.sort((a, b) => a.toIndex - b.toIndex); if (reveals.length > 1) { let currentIndex = 0; for (const r of reveals) { if (currentIndex < r.fromIndex) { redactions.push({ fromIndex: currentIndex, toIndex: r.fromIndex }); } currentIndex = r.toIndex; } redactions.push({ fromIndex: currentIndex, toIndex: response.length }); } for (const r of reveals) { if (!r.hash) { continue; } redactions.push(r); } redactions.sort((a, b) => a.toIndex - b.toIndex); return redactions; }, assertValidProviderReceipt({ receipt, params: paramsAny, logger, ctx }) { logTranscript(); let extractedParams = {}; const secretParams = ('secretParams' in paramsAny) ? paramsAny.secretParams : undefined; const newParams = substituteParamValues(paramsAny, secretParams, !secretParams); const params = newParams.newParams; extractedParams = { ...extractedParams, ...newParams.extractedValues }; const req = (0, utils_3.getHttpRequestDataFromTranscript)(receipt); if (req.method !== params.method.toLowerCase()) { throw new Error(`Invalid method: ${req.method}`); } const url = new URL(params.url); const { protocol, pathname } = url; if (protocol !== 'https:') { logger.error('params URL: %s', params.url); throw new Error(`Expected protocol: https, found: ${protocol}`); } const searchParams = params.url.includes('?') ? params.url.split('?')[1] : ''; //brackets in URL path turn into %7B and %7D, so replace them back const expectedPath = pathname.replaceAll('%7B', '{').replaceAll('%7D', '}') + ((searchParams === null || searchParams === void 0 ? void 0 : searchParams.length) ? '?' + searchParams : ''); if (!(0, utils_2.matchRedactedStrings)((0, tls_1.strToUint8Array)(expectedPath), (0, tls_1.strToUint8Array)(req.url))) { logger.error('params URL: %s', params.url); throw new Error(`Expected path: ${expectedPath}, found: ${req.url}`); } const expectedHostStr = getHostHeaderString(url); if (req.headers.host !== expectedHostStr) { throw new Error(`Expected host: ${expectedHostStr}, found: ${req.headers.host}`); } const connectionHeader = req.headers['connection']; if (connectionHeader !== 'close') { throw new Error(`Connection header must be "close", got "${connectionHeader}"`); } const serverBlocks = receipt .filter(s => s.sender === 'server') .map((r) => r.message) .filter(b => !b.every(b => b === utils_3.REDACTION_CHAR_CODE)); // filter out fully redacted blocks const response = concatArrays(...serverBlocks); let res; res = (0, utils_3.uint8ArrayToStr)(response); const okRegex = (0, utils_2.makeRegex)('^HTTP\\/1.1 2\\d{2}'); const matchRes = okRegex.exec(res); if (!matchRes) { const statusRegex = (0, utils_2.makeRegex)('^HTTP\\/1.1 (\\d{3})'); const matchRes = statusRegex.exec(res); if (matchRes && matchRes.length > 1) { throw new Error(`Provider returned error ${matchRes[1]}"`); } let lineEnd = res.indexOf('*'); if (lineEnd === -1) { lineEnd = res.indexOf('\n'); } if (lineEnd === -1) { lineEnd = OK_HTTP_HEADER.length; } throw new Error(`Response did not start with \"HTTP/1.1 2XX\" got "${res.slice(0, lineEnd)}"`); } let bodyStart; if (shouldRevealCrlf(ctx)) { bodyStart = res.indexOf('\r\n\r\n', OK_HTTP_HEADER.length) + 4; if (bodyStart < 4) { throw new Error('Response body start not found'); } } else { bodyStart = OK_HTTP_HEADER.length; } //validate server Date header if present const dateHeader = (0, utils_2.makeRegex)(dateHeaderRegex).exec(res); if ((dateHeader === null || dateHeader === void 0 ? void 0 : dateHeader.length) > 1) { const serverDate = new Date(dateHeader[1]); if ((Date.now() - serverDate.getTime()) > dateDiff) { logger.info({ dateHeader: dateHeader[0], current: Date.now() }, 'date header is off'); throw new Error(`Server date is off by "${(Date.now() - serverDate.getTime()) / 1000} s"`); } } const paramBody = params.body instanceof Uint8Array ? params.body : (0, tls_1.strToUint8Array)(params.body || ''); if (paramBody.length > 0 && !(0, utils_2.matchRedactedStrings)(paramBody, req.body)) { throw new Error('request body mismatch'); } //remove asterisks to account for chunks in the middle of revealed strings if (!secretParams) { res = res.slice(bodyStart).replace(/(\*){3,}/g, ''); } for (const { type, value, invert } of params.responseMatches || []) { const inv = Boolean(invert); // explicitly cast to boolean switch (type) { case 'regex': const regexRes = (0, utils_2.makeRegex)(value).exec(res); const match = regexRes !== null; if (match === inv) { // if both true or both false then fail throw new Error('Invalid receipt.' + ` Regex "${value}" ${invert ? 'matched' : "didn't match"}`); } if (!match) { continue; } const groups = regexRes === null || regexRes === void 0 ? void 0 : regexRes.groups; for (const paramName in groups || []) { if (paramName in extractedParams) { throw new Error(`Duplicate parameter ${paramName}`); } extractedParams[paramName] = groups[paramName]; } break; case 'contains': const includes = res.includes(value); if (includes === inv) { throw new Error(`Invalid receipt. Response ${invert ? 'contains' : 'does not contain'} "${value}"`); } break; default: throw new Error(`Invalid response match type ${type}`); } } function concatArrays(...bufs) { const totalSize = bufs.reduce((acc, e) => acc + e.length, 0); const merged = new Uint8Array(totalSize); let lenDone = 0; for (const array of bufs) { merged.set(array, lenDone); lenDone += array.length; } return merged; } return { extractedParameters: extractedParams }; function logTranscript() { const clientMsgs = receipt.filter(s => s.sender === 'client').map(m => m.message); const serverMsgs = receipt.filter(s => s.sender === 'server').map(m => m.message); const clientTranscript = utils_1.base64.encode((0, tls_1.concatenateUint8Arrays)(clientMsgs)); const serverTranscript = utils_1.base64.encode((0, tls_1.concatenateUint8Arrays)(serverMsgs)); logger.debug({ request: clientTranscript, response: serverTranscript, params: paramsAny }); } }, }; // revealing CRLF is a breaking change -- and should only be done // if the client's version supports it function shouldRevealCrlf({ version }) { return version >= api_1.AttestorVersion.ATTESTOR_VERSION_2_0_1; } function getHostPort(params, secretParams) { const { host } = new URL(getURL(params, secretParams)); if (!host) { throw new Error('url is incorrect'); } return host; } /** * Obtain the host header string from the URL. * https://stackoverflow.com/a/3364396 */ function getHostHeaderString(url) { const host = url.hostname; const port = url.port; return port && +port !== config_1.DEFAULT_HTTPS_PORT ? `${host}:${port}` : host; } const paramsRegex = /{{([^{}]+)}}/sgi; function* processRedactionRequest(body, rs, bodyStartIdx, resChunks) { let element = body; let elementIdx = 0; let elementLength = -1; if (rs.xPath) { const indexes = (0, utils_2.extractHTMLElementsIndexes)(body, rs.xPath, !!rs.jsonPath); for (const { start, end } of indexes) { element = body.slice(start, end); elementIdx = start; elementLength = end - start; if (rs.jsonPath) { yield* processJsonPath(); } else if (rs.regex) { yield* processRegexp(); } else { yield* addRedaction(); } } } else if (rs.jsonPath) { yield* processJsonPath(); } else if (rs.regex) { yield* processRegexp(); } else { throw new Error('Expected either xPath, jsonPath or regex for redaction'); } function* processJsonPath() { const jsonPathIndexes = (0, utils_2.extractJSONValueIndexes)(element, rs.jsonPath); // eslint-disable-next-line max-depth const eIndex = elementIdx; for (const ji of jsonPathIndexes) { const jStart = ji.start; const jEnd = ji.end; element = body.slice(eIndex + jStart, eIndex + jEnd); elementIdx = eIndex + jStart; elementLength = jEnd - jStart; // eslint-disable-next-line max-depth if (rs.regex) { yield* processRegexp(); } else { yield* addRedaction(); } } } function* processRegexp() { const regexp = (0, utils_2.makeRegex)(rs.regex); const elem = element || body; const match = regexp.exec(elem); // eslint-disable-next-line max-depth if (!(match === null || match === void 0 ? void 0 : match[0])) { // logger.error({ response: uint8ArrayToBinaryStr(res.body) }) throw new Error(`regexp ${rs.regex} does not match found element '${elem}'`); } elementIdx += match.index; elementLength = regexp.lastIndex - match.index; element = match[0]; if (rs.hash && (!match.groups || Object.keys(match.groups).length > 1)) { throw new Error('Exactly one named capture group is needed per hashed redaction'); } // if there are groups in the regex, // we'll only hash the group values if (!rs.hash || !match.groups) { yield* addRedaction(); return; } const fullStr = match[0]; const grp = Object.values(match.groups)[0]; const grpIdx = fullStr.indexOf(grp); // don't hash the entire regex, we'll hash the group values elementLength = grpIdx; element = fullStr.slice(0, grpIdx); yield* addRedaction(null); elementIdx += grpIdx; element = grp; elementLength = grp.length; const reveal = getReveal(elementIdx, elementLength, rs.hash); const chunkReds = (0, utils_2.getRedactionsForChunkHeaders)(reveal.fromIndex, reveal.toIndex, resChunks); if (chunkReds.length) { throw new Error('Hash redactions cannot be performed if ' + 'the redacted string is split between 2' + ' or more HTTP chunks'); } yield { reveal, redactions: chunkReds }; elementIdx += grp.length; element = fullStr.slice(grpIdx + grp.length); elementLength = element.length; yield* addRedaction(null); } // eslint-disable-next-line unicorn/consistent-function-scoping function* addRedaction(hash = rs.hash, _resChunks = resChunks) { if (elementIdx < 0 || !elementLength) { return; } const reveal = getReveal(elementIdx, elementLength, hash || undefined); yield { reveal, redactions: (0, utils_2.getRedactionsForChunkHeaders)(reveal.fromIndex, reveal.toIndex, _resChunks) }; } function getReveal(startIdx, len, hash) { const from = (0, utils_2.convertResponsePosToAbsolutePos)(startIdx, bodyStartIdx, resChunks); const to = (0, utils_2.convertResponsePosToAbsolutePos)(startIdx + len, bodyStartIdx, resChunks); return { fromIndex: from, toIndex: to, hash }; } } function substituteParamValues(currentParams, secretParams, ignoreMissingParams) { const params = JSON.parse(JSON.stringify(currentParams)); let extractedValues = {}; const hiddenURLParts = []; const urlParams = extractAndReplaceTemplateValues(params.url, ignoreMissingParams); if (urlParams) { params.url = urlParams.newParam; extractedValues = { ...urlParams.extractedValues }; if (urlParams.hiddenParts.length) { const host = getHostHeaderString(new URL(params.url)); const offset = `https://${host}`.length - currentParams.method.length - 1; //space between method and start of the path for (const hiddenURLPart of urlParams.hiddenParts) { hiddenURLParts.push({ index: hiddenURLPart.index - offset, length: hiddenURLPart.length }); } } } let bodyParams; let hiddenBodyParts = []; if (params.body) { const strBody = typeof params.body === 'string' ? params.body : (0, utils_3.uint8ArrayToStr)(params.body); bodyParams = extractAndReplaceTemplateValues(strBody, ignoreMissingParams); if (bodyParams) { params.body = bodyParams.newParam; extractedValues = { ...extractedValues, ...bodyParams.extractedValues }; hiddenBodyParts = bodyParams.hiddenParts; } } const geoParams = extractAndReplaceTemplateValues(params.geoLocation); if (geoParams) { params.geoLocation = geoParams.newParam; extractedValues = { ...extractedValues, ...geoParams.extractedValues }; } if (params.responseRedactions) { for (const r of params.responseRedactions) { if (r.regex) { const regexParams = extractAndReplaceTemplateValues(r.regex); r.regex = regexParams === null || regexParams === void 0 ? void 0 : regexParams.newParam; } if (r.xPath) { const xpathParams = extractAndReplaceTemplateValues(r.xPath); r.xPath = xpathParams === null || xpathParams === void 0 ? void 0 : xpathParams.newParam; } if (r.jsonPath) { const jsonPathParams = extractAndReplaceTemplateValues(r.jsonPath); r.jsonPath = jsonPathParams === null || jsonPathParams === void 0 ? void 0 : jsonPathParams.newParam; } } } if (params.responseMatches) { for (const r of params.responseMatches) { if (r.value !== '') { const matchParam = extractAndReplaceTemplateValues(r.value); r.value = matchParam === null || matchParam === void 0 ? void 0 : matchParam.newParam; extractedValues = { ...extractedValues, ...matchParam === null || matchParam === void 0 ? void 0 : matchParam.extractedValues }; } } } return { newParams: params, extractedValues: extractedValues, hiddenBodyParts: hiddenBodyParts, hiddenURLParts: hiddenURLParts }; function extractAndReplaceTemplateValues(param, ignoreMissingParams) { if (!param) { return null; } //const paramNames: Set<string> = new Set() const extractedValues = {}; const hiddenParts = []; let totalOffset = 0; param = param.replace(paramsRegex, (match, pn, offset) => { if (params.paramValues && pn in params.paramValues) { extractedValues[pn] = params.paramValues[pn]; totalOffset += params.paramValues[pn].length - match.length; return params.paramValues[pn]; } else if (secretParams) { if ((secretParams === null || secretParams === void 0 ? void 0 : secretParams.paramValues) && pn in (secretParams === null || secretParams === void 0 ? void 0 : secretParams.paramValues)) { hiddenParts.push({ index: offset + totalOffset, length: secretParams.paramValues[pn].length, }); totalOffset += secretParams.paramValues[pn].length - match.length; return secretParams.paramValues[pn]; } else { throw new Error(`parameter's "${pn}" value not found in paramValues and secret parameter's paramValues`); } } else { if (!(!!ignoreMissingParams)) { throw new Error(`parameter's "${pn}" value not found in paramValues`); } else { return match; } } }); return { newParam: param, extractedValues: extractedValues, hiddenParts: hiddenParts }; } } function getGeoLocation(v2Params, secretParams) { if (v2Params === null || v2Params === void 0 ? void 0 : v2Params.geoLocation) { const paramNames = new Set(); let geo = v2Params.geoLocation; //extract param names let match = null; while (match = paramsRegex.exec(geo)) { paramNames.add(match[1]); } for (const pn of paramNames) { if (v2Params.paramValues && pn in v2Params.paramValues) { geo = geo === null || geo === void 0 ? void 0 : geo.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString()); } else if ((secretParams === null || secretParams === void 0 ? void 0 : secretParams.paramValues) && pn in secretParams.paramValues) { geo = geo === null || geo === void 0 ? void 0 : geo.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString()); } else { throw new Error(`parameter "${pn}" value not found in templateParams`); } } const geoRegex = /^[A-Za-z]{2}$/sgiu; if (!geoRegex.test(geo)) { throw new Error(`Geolocation ${geo} is invalid`); } return geo; } return undefined; } function getURL(v2Params, secretParams) { let hostPort = v2Params === null || v2Params === void 0 ? void 0 : v2Params.url; const paramNames = new Set(); //extract param names let match = null; while (match = paramsRegex.exec(hostPort)) { paramNames.add(match[1]); } for (const pn of paramNames) { if (v2Params.paramValues && pn in v2Params.paramValues) { hostPort = hostPort === null || hostPort === void 0 ? void 0 : hostPort.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString()); } else if ((secretParams === null || secretParams === void 0 ? void 0 : secretParams.paramValues) && pn in secretParams.paramValues) { hostPort = hostPort === null || hostPort === void 0 ? void 0 : hostPort.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString()); } else { throw new Error(`parameter "${pn}" value not found in templateParams`); } } return hostPort; } exports.default = HTTP_PROVIDER; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2h0dHAvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSw4Q0FBeUg7QUFDekgsNENBQXlDO0FBQ3pDLHVDQUFtRTtBQUNuRSx1Q0FBK0M7QUFDL0Msb0RBUWlDO0FBRWpDLHFDQU1rQjtBQUVsQixNQUFNLGNBQWMsR0FBRyxjQUFjLENBQUE7QUFDckMsTUFBTSxlQUFlLEdBQUcsd0tBQXdLLENBQUE7QUFDaE0sTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUEsQ0FBQywwQkFBMEI7QUFNMUQsTUFBTSxhQUFhLEdBQXFCO0lBQ3ZDLFFBQVEsRUFBRSxXQUFXO0lBQ3JCLGtCQUFrQixDQUFDLE1BQU07UUFDeEIsT0FBTyxDQUFDLG9CQUFvQixJQUFJLE1BQU0sQ0FBQztZQUN0QyxDQUFDLENBQUMsTUFBTSxDQUFDLGtCQUFrQjtZQUMzQixDQUFDLENBQUMsU0FBUyxDQUFBO0lBQ2IsQ0FBQztJQUNELFdBQVcsQ0FBQyxNQUFNLEVBQUUsWUFBWTtRQUMvQixPQUFPLENBQUMsYUFBYSxJQUFJLE1BQU0sQ0FBQztZQUMvQixDQUFDLENBQUMsY0FBYyxDQUFDLE1BQU0sRUFBRSxZQUFZLENBQUM7WUFDdEMsQ0FBQyxDQUFDLFNBQVMsQ0FBQTtJQUNiLENBQUM7SUFDRCx1QkFBdUIsQ0FBQyxNQUFNO1FBQzdCLElBQUksY0FBYyxHQUF5QjtZQUMxQyx5QkFBeUIsRUFBRyxDQUFDLFVBQVUsQ0FBQztTQUN4QyxDQUFBO1FBQ0QsSUFBRyx5QkFBeUIsSUFBSSxNQUFNLEVBQUUsQ0FBQztZQUN4QyxjQUFjLEdBQUc7Z0JBQ2hCLEdBQUcsY0FBYztnQkFDakIsR0FBRyxNQUFNLENBQUMsdUJBQXVCO2FBQ2pDLENBQUE7UUFDRixDQUFDO1FBRUQsT0FBTyxjQUFjLENBQUE7SUFDdEIsQ0FBQztJQUNELGFBQWEsQ0FB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