UNPKG

firebase-rules-parser

Version:
834 lines 67.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Tree_1 = require("antlr4/tree/Tree"); const merge = require("deepmerge"); const __1 = require(".."); const FirebaseRulesListener_1 = require("../parser/FirebaseRulesListener"); const FirebaseRulesParser_1 = require("../parser/FirebaseRulesParser"); const FirestoreRuleClosure_1 = require("./FirestoreRuleClosure"); const MockFirestoreRequest_1 = require("./MockFirestoreRequest"); const MockFirestoreResource_1 = require("./MockFirestoreResource"); const system_1 = require("./system"); const patternMatch_1 = require("./utils/patternMatch"); var StackItemType; (function (StackItemType) { StackItemType["ALLOW"] = "allow"; StackItemType["ARITHMETIC"] = "arithmetic"; StackItemType["ARRAY"] = "array"; StackItemType["ARRAY_CELL_REF"] = "array-cell-ref"; StackItemType["BINARY"] = "binary"; StackItemType["COMPARE"] = "compare"; StackItemType["CLOSURE"] = "closure"; StackItemType["EXPRESSION"] = "expression"; StackItemType["FUNCTION_CALL"] = "function-call"; StackItemType["FUNCTION_DECLARATION"] = "fun-dec"; StackItemType["GET"] = "get"; StackItemType["MEMBER_FIELD_REF"] = "identifier-field-ref"; StackItemType["LOGICAL"] = "logical"; StackItemType["OBJECT_REFERENCE"] = "objectref"; StackItemType["PARENTHESIS"] = "parenthesis"; StackItemType["RESOLVED"] = "resolved"; StackItemType["UNARY"] = "unary"; StackItemType["VALUE"] = "value"; })(StackItemType || (StackItemType = {})); exports.defaultFirebaseRulesContext = { auth: merge(MockFirestoreRequest_1.defaultFirestoreRequest.auth, {}), resource: merge(MockFirestoreResource_1.defaultFirestoreResource, {}), }; // tslint:disable-next-line: jsdoc-format /** * Create a default firebare rule context to be used when calling rules rights. * * Function uses deep merge, so you can set needed values in sub objects like, * ```typescript * createFirebaseRulesContext({ * auth: { * uid: '123' * } * }); * ``` * This will override only the uid property and will leave other properties intact. * * @export * @param {Partial<FirebaseRulesContext>} [overrides] Values, to be overrided from default values. * @param {boolean} authenticated When true, a default mock user info is given for context. Default value is `false`. * @returns {FirebaseRulesContext} */ function createFirebaseRulesContext(overrides, authenticated = false) { const context = merge(exports.defaultFirebaseRulesContext, authenticated ? {} : { auth: { uid: null, email: null, }, }); return overrides ? merge(context, overrides) : context; } exports.createFirebaseRulesContext = createFirebaseRulesContext; /** * Firebase Rules Intepreter testing user rights based on rules script */ class FirebaseRulesIntepreter { constructor() { this.init = (rulesFile) => { this._parser.init(rulesFile); return this; }; /** * Elaborate access rights for given path within given context * * @memberof RulesParser */ this.hasAccess = (path, context) => { return this._parser.hasAccess(path, context); }; this._parser = new _FirebaseRulesIntepreter(); } get request() { return this._parser.request; } set request(value) { this._parser.request = value; } get resource() { return this._parser.resource; } set resource(value) { this._parser.resource = value; } /** * Get the namespace used in rules -file * * @readonly * @memberof RulesParser */ get namespace() { return this._parser.namespace; } } exports.FirebaseRulesIntepreter = FirebaseRulesIntepreter; class _FirebaseRulesIntepreter extends FirebaseRulesListener_1.FirebaseRulesListener { constructor() { super(); this._request = MockFirestoreRequest_1.defaultFirestoreRequest; this._resource = MockFirestoreResource_1.defaultFirestoreResource; this.allowRules = []; this._stack = []; // private closure: FirestoreRulesClosureContext = new FirestoreRulesClosureContext(); this.pathElements = []; this.init = (rulesFile) => { this._stack = []; this._parser = __1.parseFirebaseRulesFromString(rulesFile); this._parser.addErrorListener({ syntaxError: (recognizer, offendingSymbol, line, column, msg, e) => { // tslint:disable-next-line: no-console console.error(`${msg} at line ${line} column ${column}`); const source = this.parseSourceToLines(rulesFile); // tslint:disable-next-line: no-console console.error(source[line - 1]); const spaces = ' '.repeat(column - 1); // tslint:disable-next-line: no-console console.error(spaces + '^'); }, reportAmbiguity: (recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs) => { // tslint:disable-next-line: no-console console.error(`Ambiguity: ' + ${ambigAlts} at line ${startIndex} column ${stopIndex}`); }, reportAttemptingFullContext: (recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs) => { // tslint:disable-next-line: no-console console.error(conflictingAlts); }, reportContextSensitivity: (recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs) => { // tslint:disable-next-line: no-console console.error(conflictingAlts); }, }); this.walkAST(this._parser); return this; }; /** * Elaborate access rights for given path within given context * * @memberof RulesParser */ this.hasAccess = (path, context) => { const moduleClosure = this.newClosure(); const internalContext = Object.assign({}, context, { hasAccess: {}, exit: false, path }); this._stack[0].callback(internalContext, moduleClosure); return internalContext.hasAccess; }; this.enterService = (ctx) => { const callbacks = []; this._namespace = ctx.getChild(1).getText(); this._stack.push({ type: StackItemType.CLOSURE, debug: 'service', obj: callbacks, callback: (context, closure) => this.executeClosure(context, closure, callbacks), }); }; this.enterMatcher = (ctx) => { if (ctx.getChildCount() > 0) { const path = ctx.getChild(1); const callbacks = []; const pathElement = this.generatePath(path); this._stack.push({ type: StackItemType.CLOSURE, debug: ctx.getText(), obj: callbacks, callback: (context, closure) => { const newClosure = closure.open(); newClosure.path = pathElement; const matchPattern = new patternMatch_1.MatchPattern(newClosure.getPath()); // TODO optimize const variables = matchPattern.matchPrefix(context.path); if (variables) { newClosure.addValues(variables); this.executeClosure(context, newClosure, callbacks); } }, }); } else { throw new Error('Internal error: match -element without child elements.'); } }; this.exitMatcher = (ctx) => { // this.pathElements.pop(); // this.closure.close(); const closure = this._stack.pop(); const parentClosure = this.peek(); parentClosure.obj.push(closure.callback); }; this.enterAllow = (ctx) => { const path = this.getCurrentPath(); const pattern = new patternMatch_1.MatchPattern(path); const allowKeys = []; const currentAllowRule = { pattern, allowKeys, if: (context, closure) => false, }; this._stack.push({ type: StackItemType.ALLOW, obj: currentAllowRule, debug: ctx.getText(), callback: (context, closure) => { const hasAccess = currentAllowRule.if(context, closure); for (const key of allowKeys) { context.hasAccess[key] = context.hasAccess[key] || hasAccess; } }, }); // this.allowRules.push(currentAllowRule); }; this.exitAllow = (ctx) => { const item = this._stack.pop(); if (!item) { throwError('Internal error', ctx); return; } if (item.type !== StackItemType.ALLOW) { const allowItem = this._stack.pop(); if (!allowItem) { throwError('Internal error', ctx); return; } allowItem.obj.if = item.callback; const closure = this.peek(); closure.obj.push(allowItem.callback); } else { // No expression is give, so allow will treated as true for all values const closure = this.peek(); closure.obj.push(() => true); } }; this.enterAllowKey = (ctx) => { const item = this.peek(); const currentAllowRule = item.obj; if (!item.obj) { throwError('Allow key defined while no allow operation is active.', ctx); return; } currentAllowRule.allowKeys.push(ctx.getText()); }; this.exitCompareExpression = (ctx) => { this.handleBinaryOperation(StackItemType.COMPARE, ctx); }; this.exitInExpression = (ctx) => { this.handleBinaryOperation(StackItemType.ARITHMETIC, ctx); }; this.exitArithmeticExpression = (ctx) => { this.handleBinaryOperation(StackItemType.ARITHMETIC, ctx); }; this.exitLogicalExpression = (ctx) => { this.handleBinaryOperation(StackItemType.LOGICAL, ctx); }; this.exitBinaryExpression = (ctx) => { this.handleBinaryOperation(StackItemType.BINARY, ctx); }; this.exitUnaryExpression = (ctx) => { const value = this._stack.pop(); // toto undefined const operator = ctx.getChild(0).symbol.text; this._stack.push({ type: StackItemType.UNARY, debug: ctx.getText(), callback: this.resolveUnaryOperation(operator, value, ctx), }); }; this.enterArrayExpression = (ctx) => { const callback = (context, closure) => { return arrayItem.items.map(expression => expression(context, closure)); }; const arrayItem = { type: StackItemType.ARRAY, debug: ctx.getText(), items: [], callback, }; this.push(arrayItem); }; this.exitArrayExpression = (ctx) => { const items = []; while (this.peek().type !== StackItemType.ARRAY) { items.unshift(this.pop().callback); } const item = this.peek(); item.items = items; }; this.exitMemberReferenceExpression = (ctx) => { const expression = this._stack.pop(); const fieldName = ctx.getChild(2).getText(); const item = { type: StackItemType.MEMBER_FIELD_REF, debug: ctx.getText(), callback: (context, closure) => { const value = expression.callback(context, closure); return value[fieldName]; }, }; this.push(item); }; this.exitRangeExpression = (ctx) => { const arrayExpression = this.pop(); const expression = this.pop(); const item = { type: StackItemType.ARRAY_CELL_REF, debug: ctx.getText(), callback: (context, closure) => { const index = arrayExpression.callback(context, closure); const value = expression.callback(context, closure); return value[index]; }, }; this.push(item); }; this.enterMemberFunctionExpression = (ctx) => { const functionName = ctx.getChild(2).getText(); const argCallbacks = []; // const closure = this.closure.current; const itemContext = { callbacks: argCallbacks, expression: undefined, }; this._stack.push({ type: StackItemType.FUNCTION_CALL, debug: ctx.getText(), obj: itemContext, callback: (context, closure) => { const fun = closure.getValue(functionName); if (!fun) { throwError(`Function with name ${functionName} was not found. Please, make sure that the function name is correctly spelled and it is available in this scope.`, ctx); } const self = itemContext.expression.callback(context, closure); // const args: any[] = []; let index = 0; const funClosure = closure.open(); for (const callback of argCallbacks) { const callbackResult = callback(context, funClosure); // args.push(callbackResult); funClosure.self[fun.argNames[index++]] = callbackResult; } const value = fun.callback(context, funClosure, self); funClosure.close(); return value; }, }); }; this.exitMemberFunctionExpression = (ctx) => { const expression = this._stack.pop(); const item = this.peek(); item.obj.expression = expression; }; this.enterFunctionCall = (ctx) => { const functionName = ctx.getChild(0).getText(); const argExpressions = []; const fieldRefs = []; const functionCallItem = { type: StackItemType.FUNCTION_CALL, debug: ctx.getText(), argExpressions, callback: (context, closure) => { const fun = closure.getValue(functionName); if (!fun) { throwError(`Function with name ${functionName} was not found. Please, make sure that the function name is correctly spelled and it is available in this scope.`, ctx); } let index = 0; const funClosure = closure.open(); for (const callback of argExpressions) { const callbackResult = callback(context, closure); funClosure.self[fun.argNames[index++]] = callbackResult; } let value = fun.callback(context, funClosure); funClosure.close(); if (fieldRefs.length > 0) { value = this.getFieldValueFromObject(context, value, fieldRefs, 0, closure); if (value) { return value; } throwError(`Null value error, field ${ctx.getText()} do not exists`, ctx); } return value; }, }; this.push(functionCallItem); }; this.exitArg = (ctx) => { const expression = this.pop(); const item = this.peek(); if (item.type !== StackItemType.FUNCTION_CALL) { throw new Error('Expecting a function call but found ' + expression.type); } const argCallbacks = item.argExpressions; argCallbacks.push(expression.callback); }; this.exitMemberArg = (ctx) => { const expression = this._stack.pop(); const item = this.peek(1); const argCallbacks = item.obj.callbacks; if (!item.obj) { throwError('Allow key defined while no allow operation is active.', ctx); return; } argCallbacks.push(expression.callback); }; this.enterFunctionDeclaration = (ctx) => { this._stack.push({ type: StackItemType.FUNCTION_DECLARATION, callback: () => true, obj: [], debug: '', }); }; this.exitArgDeclaration = (ctx) => { const item = this.peek(); item.obj.push(ctx.getText()); }; this.exitFunctionDeclaration = (ctx) => { const funcBody = this._stack.pop(); const funDec = this._stack.pop(); const parentClosure = this.peek(); const argNames = funDec.obj; if (!funcBody) { throwError(`Function not found from stack`, ctx); return; } const functionName = ctx.getChild(1).symbol.text; // TODO check from closure item // if (this.closure.current.self[functionName]) { // throwError(`Function with name ${functionName} already exists`, ctx); // return; // } parentClosure.obj.push((context, closure) => { const desc = { callback: funcBody.callback, argNames, }; closure.addValues({ [functionName]: desc, }); return funcBody.callback(context, closure); }); }; this.exitNumberExpression = (ctx) => { this.handleValueExpression(ctx); }; this.exitStringExpression = (ctx) => { let value = ctx.getText(); value = value.substr(1, value.length - 2); this._stack.push({ type: StackItemType.VALUE, debug: ctx.getText(), callback: () => value, }); }; this.exitBooleanExpression = (ctx) => { this._stack.push({ type: StackItemType.VALUE, callback: () => JSON.parse(ctx.getText()), debug: ctx.getText(), }); }; this.exitNullExpression = (ctx) => { this._stack.push({ type: StackItemType.VALUE, callback: () => null, debug: ctx.getText(), }); }; this.enterObjectReference = (ctx) => { const identifier = ctx.getChild(0).getText(); const fieldRefs = [identifier]; // const closure = this.closure.current; this._stack.push({ type: StackItemType.OBJECT_REFERENCE, obj: fieldRefs, debug: ctx.getText(), callback: (context, closure) => { const objectIdentifier = this.refValue(context, fieldRefs[0], closure); let obj = closure.getValue(objectIdentifier); obj = this.getFieldValueFromObject(context, obj, fieldRefs, 1, closure); if (obj || obj === '') { return obj; } throwError(`Null value error, field ${ctx.getText()} do not exists`, ctx); }, }); }; this.exitObjectReferenceExpression = (ctx) => { const fieldName = ctx.getText(); this._stack.push({ type: StackItemType.OBJECT_REFERENCE, debug: ctx.getText(), callback: (context, closure) => { const objectIdentifier = this.refValue(context, fieldName, closure); return closure.getValue(objectIdentifier); }, }); }; this.enterRuleFunctionCall = (ctx) => { const functionName = ctx.getChild(0).getText(); const argCallbacks = []; const fieldRefs = []; // const closure = this.closure.current; this._stack.push({ type: StackItemType.FUNCTION_CALL, debug: ctx.getText(), obj: { callbacks: argCallbacks, fieldRefs, }, callback: (context, closure) => { let fun; switch (functionName) { case 'get': fun = context.onGetCall || (() => { // tslint:disable-next-line throwError('get -function called but there is no onGetCall handler degined on context. Please override onGetCall trigger on context.', ctx); }); break; case 'exists': fun = context.onExistsCall || // tslint:disable-next-line (() => { throwError('exists -function called but there is no onGetCall handler degined on context. Please override onExistCall trigger on context.', ctx); }); break; default: throwError(`Unidentified function ${functionName}`, ctx); return; } let path = ''; for (const callback of argCallbacks) { const callbackResult = typeof callback === 'string' ? callback : callback(context, closure); path += '/' + callbackResult; } let value = fun(path); if (fieldRefs.length > 0) { value = this.getFieldValueFromObject(context, value, fieldRefs, 0, closure); if (value) { return value; } throwError(`Null value error, field ${ctx.getText()} do not exists`, ctx); } return value; }, }); }; this.exitGetPathExpressionVariable = (ctx) => { const expression = this._stack.pop(); const item = this.peek(); if (!item.obj) { throwError('Allow key defined while no allow operation is active.', ctx); return; } const argCallbacks = item.obj.callbacks; argCallbacks.push(expression.callback); }; this.exitGetPathVariable = (ctx) => { const item = this.peek(); const value = ctx.getText(); const argCallbacks = item.obj.callbacks; if (!item.obj) { throwError('Allow key defined while no allow operation is active.', ctx); return; } argCallbacks.push(value); }; this.exitFieldReferenceWithIdentifier = (ctx) => { const fieldName = ctx.getChild(1).getText(); const expressionItem = this.pop(); const fieldRefItem = { type: StackItemType.MEMBER_FIELD_REF, debug: ctx.getText(), callback: (context, closure) => { const value = expressionItem.callback(context, closure); if (typeof value !== 'object') { throwError(`Unidentfied type ${typeof value} for field ref.`, ctx); return; } return value[fieldName]; }, }; this.push(fieldRefItem); }; this.exitFieldReferenceWithMemberRef = (ctx) => { const expression = this._stack.pop(); if (!expression) { throwError('Internal error, no expression found in memberRef', ctx); return; } const item = this.peek(); item.obj.push(expression.callback); }; this.executeClosure = (context, closure, callbacks) => { if (context.exit) { return; } const subClosure = closure.open(); for (const callback of callbacks) { callback(context, subClosure); if (context.exit) { return; } } }; this.refValue = (context, value, closure) => { if (typeof value === 'string') { return value; } return value(context, closure); }; this.getFieldValueFromObject = (context, obj, fieldRefs, startIndex, closure) => { for (let i = startIndex; i < fieldRefs.length; i++) { if (!obj) { break; } const value = this.refValue(context, fieldRefs[i], closure); obj = obj[value]; } return obj; }; this.parseSourceToLines = (source) => source.split('\n'); this.generatePath = (path) => { let result = ''; for (let i = 0; i < path.getChildCount(); i++) { const child = path.getChild(i); if (child instanceof FirebaseRulesParser_1.PathVariableContext) { for (let j = 0; j < child.getChildCount(); j++) { result += child.getChild(j).symbol.text; } } else { result += child.symbol.text; } } return result; }; this.resolveUnaryOperation = (operation, expression, ctx) => { if (!(expression && operation)) { throwError(`Internal error`, ctx); } return (context, closure) => { const value = expression.callback(context, closure); switch (operation) { case '!': return !value; case '-': return -value; default: throw new Error(`Unidentified operator: ${operation} at line ${ctx.start.line} column ${ctx.start.start}.`); } }; }; this.resolveBinaryOperation = (left, operator, right, ctx) => { if (!(right && left)) { throwError(`Internal error`, ctx); } return (context, closure) => { const leftValue = left.callback(context, closure); const rightValue = right.callback(context, closure); switch (operator) { case '<': return leftValue < rightValue; case '<=': return leftValue <= rightValue; case 'in': { if (Array.isArray(rightValue)) { // List in operator => check if an array has a value return rightValue.includes(leftValue); } if (typeof rightValue === 'object') { // Map in operator => check if the object has a key return rightValue.hasOwnProperty(leftValue); } throwError('In operatation must have a list or and map as target, but found type of ' + typeof rightValue, ctx); return false; } case '==': if (Array.isArray(leftValue)) { if (Array.isArray(rightValue)) { if (leftValue.length !== rightValue.length) { return false; } for (let i = 0; i < leftValue.length; i++) { if (leftValue[i] !== rightValue[i]) { return false; } } return true; } return false; } if (Array.isArray(rightValue)) { return false; } return leftValue === rightValue; case '!=': return leftValue !== rightValue; case '>': return leftValue > rightValue; case '>=': return leftValue >= rightValue; case '+': return leftValue + rightValue; case '-': return leftValue - rightValue; case '*': return leftValue * rightValue; case '/': return leftValue / rightValue; case '%': return leftValue % rightValue; case '&&': return leftValue && rightValue; case '||': return leftValue || rightValue; case '^': return leftValue ^ rightValue; default: throw new Error(`Unidentified operator: ${operator} at line ${ctx.start.line} column ${ctx.start.start}.`); } }; }; this.getCurrentPath = () => { let result = ''; for (const pathElement of this.pathElements) { result += pathElement; } return result; }; /** * Map rules in file to paths to be accessed * * @private * @memberof RulesParser */ this.walkAST = (parser) => { const service = parser.service(); Tree_1.ParseTreeWalker.DEFAULT.walk(this, service); }; this.initGlobalClosure = () => { this._globalClosure = new FirestoreRuleClosure_1.FirestoreRulesClosure(); system_1.default(this._globalClosure); }; this.initGlobalClosure(); } get request() { return this._request; } set request(value) { this._request = value; } get resource() { return this._resource; } set resource(value) { this._resource = value; } /** * Get the namespace used in rules -file * * @readonly * @memberof RulesParser */ get namespace() { return this._namespace; } defaultResult() { return 0; } push(item) { this._stack.push(item); } pop() { const item = this._stack.pop(); return this.stackItemWithType(item); } stackItemWithType(item) { switch (item.type) { case StackItemType.FUNCTION_CALL: return item; case StackItemType.CLOSURE: return item; default: return item; } } newClosure() { const closure = new FirestoreRuleClosure_1.FirestoreRulesClosure(this._globalClosure); closure.self.request = this.request; closure.self.resource = this.resource; return closure; } peek(distance = 0) { return this.stackItemWithType(this._stack[this._stack.length - 1 - distance]); } handleValueExpression(ctx) { const value = JSON.parse(ctx.getText()); this._stack.push({ type: StackItemType.VALUE, debug: ctx.getText(), callback: () => value, }); } handleBinaryOperation(type, ctx) { const right = this._stack.pop(); const left = this._stack.pop(); const operator = ctx.getChild(1).getText(); this._stack.push({ type, debug: operator, callback: this.resolveBinaryOperation(left, operator, right, ctx), }); } } function throwError(message, ctx) { throw new Error(`${message} at line ${ctx.start.line} column ${ctx.start.start}. ${ctx.getSourceInterval().toString()}`); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW50ZXByZXRlci9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUNBLDJDQUFtRDtBQUNuRCxtQ0FBbUM7QUFFbkMsMEJBQWtEO0FBQ2xELDJFQUF3RTtBQUN4RSx1RUFnQ3VDO0FBR3ZDLGlFQUErRDtBQUMvRCxpRUFBdUY7QUFDdkYsbUVBQTBGO0FBQzFGLHFDQUErQztBQUMvQyx1REFBb0Q7QUFZcEQsSUFBSyxhQW1CSjtBQW5CRCxXQUFLLGFBQWE7SUFDaEIsZ0NBQWUsQ0FBQTtJQUNmLDBDQUF5QixDQUFBO0lBQ3pCLGdDQUFlLENBQUE7SUFDZixrREFBaUMsQ0FBQTtJQUNqQyxrQ0FBaUIsQ0FBQTtJQUNqQixvQ0FBbUIsQ0FBQTtJQUNuQixvQ0FBbUIsQ0FBQTtJQUNuQiwwQ0FBeUIsQ0FBQTtJQUN6QixnREFBK0IsQ0FBQTtJQUMvQixpREFBZ0MsQ0FBQTtJQUNoQyw0QkFBVyxDQUFBO0lBQ1gsMERBQXlDLENBQUE7SUFDekMsb0NBQW1CLENBQUE7SUFDbkIsK0NBQThCLENBQUE7SUFDOUIsNENBQTJCLENBQUE7SUFDM0Isc0NBQXFCLENBQUE7SUFDckIsZ0NBQWUsQ0FBQTtJQUNmLGdDQUFlLENBQUE7QUFDakIsQ0FBQyxFQW5CSSxhQUFhLEtBQWIsYUFBYSxRQW1CakI7QUErRFksUUFBQSwyQkFBMkIsR0FBa0M7SUFDeEUsSUFBSSxFQUFFLEtBQUssQ0FBQyw4Q0FBdUIsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDO0lBQzdDLFFBQVEsRUFBRSxLQUFLLENBQUMsZ0RBQXdCLEVBQUUsRUFBRSxDQUFDO0NBQzlDLENBQUM7QUFNRix5Q0FBeUM7QUFDekM7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBQ0gsU0FBZ0IsMEJBQTBCLENBQ3hDLFNBQXlDLEVBQ3pDLGdCQUF5QixLQUFLO0lBRTlCLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FDbkIsbUNBQTJC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