@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
JavaScript
"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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