@aws-cdk/aws-iam
Version:
CDK routines for easily assigning correct and minimal IAM permissions
169 lines • 22.4 kB
JavaScript
// IAM Statement merging
//
// See docs/policy-merging.als for a formal model of the logic
// implemented here.
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeStatements = void 0;
const policy_statement_1 = require("../policy-statement");
const util_1 = require("../util");
const comparable_principal_1 = require("./comparable-principal");
/*
* Don't produce any merged statements larger than this.
*
* They will become impossible to divide across managed policies if we do,
* and this is the maximum size for User policies.
*/
const MAX_MERGE_SIZE = 2000;
/**
* Merge as many statements as possible to shrink the total policy doc, modifying the input array in place
*
* We compare and merge all pairs of statements (O(N^2) complexity), opportunistically
* merging them. This is not guaranteed to produce the optimal output, but it's probably
* Good Enough(tm). If it merges anything, it's at least going to produce a smaller output
* than the input.
*/
function mergeStatements(scope, statements, limitSize) {
const sizeOptions = policy_statement_1.deriveEstimateSizeOptions(scope);
const compStatements = statements.map(makeComparable);
// Keep trying until nothing changes anymore
while (onePass()) { /* again */ }
const mergedStatements = new Array();
const originsMap = new Map();
for (const comp of compStatements) {
const statement = renderComparable(comp);
mergedStatements.push(statement);
originsMap.set(statement, comp.originals);
}
return { mergedStatements, originsMap };
// Do one optimization pass, return 'true' if we merged anything
function onePass() {
let ret = false;
for (let i = 0; i < compStatements.length; i++) {
let j = i + 1;
while (j < compStatements.length) {
const merged = tryMerge(compStatements[i], compStatements[j], limitSize, sizeOptions);
if (merged) {
compStatements[i] = merged;
compStatements.splice(j, 1);
ret = true;
}
else {
j++;
}
}
}
return ret;
}
}
exports.mergeStatements = mergeStatements;
/**
* Given two statements, return their merging (if possible)
*
* We can merge two statements if:
*
* - Their effects are the same
* - They don't have Sids (not really a hard requirement, but just a simplification and an escape hatch)
* - Their Conditions are the same
* - Their NotAction, NotResource and NotPrincipal sets are the same (empty sets is fine).
* - From their Action, Resource and Principal sets, 2 are subsets of each other
* (empty sets are fine).
*/
function tryMerge(a, b, limitSize, options) {
// Effects must be the same
if (a.statement.effect !== b.statement.effect) {
return;
}
// We don't merge Sids (for now)
if (a.statement.sid || b.statement.sid) {
return;
}
if (a.conditionString !== b.conditionString) {
return;
}
if (!setEqual(a.statement.notActions, b.statement.notActions) ||
!setEqual(a.statement.notResources, b.statement.notResources) ||
!setEqualPrincipals(a.statement.notPrincipals, b.statement.notPrincipals)) {
return;
}
// We can merge these statements if 2 out of the 3 sets of Action, Resource, Principal
// are the same.
const setsEqual = (setEqual(a.statement.actions, b.statement.actions) ? 1 : 0) +
(setEqual(a.statement.resources, b.statement.resources) ? 1 : 0) +
(setEqualPrincipals(a.statement.principals, b.statement.principals) ? 1 : 0);
if (setsEqual < 2 || unmergeablePrincipals(a, b)) {
return;
}
const combined = a.statement.copy({
actions: setMerge(a.statement.actions, b.statement.actions),
resources: setMerge(a.statement.resources, b.statement.resources),
principals: setMergePrincipals(a.statement.principals, b.statement.principals),
});
if (limitSize && combined._estimateSize(options) > MAX_MERGE_SIZE) {
return undefined;
}
return {
originals: [...a.originals, ...b.originals],
statement: combined,
conditionString: a.conditionString,
};
}
/**
* Calculate and return cached string set representation of the statement elements
*
* This is to be able to do comparisons on these sets quickly.
*/
function makeComparable(s) {
return {
originals: [s],
statement: s,
conditionString: JSON.stringify(s.conditions),
};
}
/**
* Return 'true' if the two principals are unmergeable
*
* This only happens if one of them is a literal, untyped principal (typically,
* `Principal: '*'`) and the other one is typed.
*
* `Principal: '*'` behaves subtly different than `Principal: { AWS: '*' }` and must
* therefore be preserved.
*/
function unmergeablePrincipals(a, b) {
const aHasLiteral = a.statement.principals.some(v => util_1.LITERAL_STRING_KEY in v.policyFragment.principalJson);
const bHasLiteral = b.statement.principals.some(v => util_1.LITERAL_STRING_KEY in v.policyFragment.principalJson);
return aHasLiteral !== bHasLiteral;
}
/**
* Turn a ComparableStatement back into a Statement
*/
function renderComparable(s) {
return s.statement;
}
/**
* Whether the given sets are equal
*/
function setEqual(a, b) {
const bSet = new Set(b);
return a.length === b.length && a.every(k => bSet.has(k));
}
/**
* Merge two value sets
*/
function setMerge(x, y) {
return Array.from(new Set([...x, ...y])).sort();
}
function setEqualPrincipals(xs, ys) {
const xPrincipals = comparable_principal_1.partitionPrincipals(xs);
const yPrincipals = comparable_principal_1.partitionPrincipals(ys);
const nonComp = setEqual(xPrincipals.nonComparable, yPrincipals.nonComparable);
const comp = setEqual(Object.keys(xPrincipals.comparable), Object.keys(yPrincipals.comparable));
return nonComp && comp;
}
function setMergePrincipals(xs, ys) {
const xPrincipals = comparable_principal_1.partitionPrincipals(xs);
const yPrincipals = comparable_principal_1.partitionPrincipals(ys);
const comparable = { ...xPrincipals.comparable, ...yPrincipals.comparable };
return [...Object.values(comparable), ...xPrincipals.nonComparable, ...yPrincipals.nonComparable];
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVyZ2Utc3RhdGVtZW50cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1lcmdlLXN0YXRlbWVudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHdCQUF3QjtBQUN4QixFQUFFO0FBQ0YsOERBQThEO0FBQzlELG9CQUFvQjs7O0FBSXBCLDBEQUFzRztBQUV0RyxrQ0FBNkM7QUFDN0MsaUVBQTZEO0FBRzdEOzs7OztHQUtHO0FBQ0gsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDO0FBRTVCOzs7Ozs7O0dBT0c7QUFDSCxTQUFnQixlQUFlLENBQUMsS0FBaUIsRUFBRSxVQUE2QixFQUFFLFNBQWtCO0lBQ2xHLE1BQU0sV0FBVyxHQUFHLDRDQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3JELE1BQU0sY0FBYyxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLENBQUM7SUFFdEQsNENBQTRDO0lBQzVDLE9BQU8sT0FBTyxFQUFFLEVBQUUsRUFBRSxXQUFXLEVBQUU7SUFFakMsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLEtBQUssRUFBbUIsQ0FBQztJQUN0RCxNQUFNLFVBQVUsR0FBRyxJQUFJLEdBQUcsRUFBc0MsQ0FBQztJQUNqRSxLQUFLLE1BQU0sSUFBSSxJQUFJLGNBQWMsRUFBRTtRQUNqQyxNQUFNLFNBQVMsR0FBRyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDakMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO0tBQzNDO0lBRUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFVBQVUsRUFBRSxDQUFDO0lBRXhDLGdFQUFnRTtJQUNoRSxTQUFTLE9BQU87UUFDZCxJQUFJLEdBQUcsR0FBRyxLQUFLLENBQUM7UUFFaEIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFH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NUQsTUFBTSxXQUFXLEdBQUcsMENBQW1CLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDNUMsTUFBTSxXQUFXLEdBQUcsMENBQW1CLENBQUMsRUFBRSxDQUFDLENBQUM7SUFFNUMsTUFBTSxVQUFVLEdBQUcsRUFBRSxHQUFHLFdBQVcsQ0FBQyxVQUFVLEVBQUUsR0FBRyxXQUFXLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDNUUsT0FBTyxDQUFDLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsRUFBRSxHQUFHLFdBQVcsQ0FBQyxhQUFhLEVBQUUsR0FBRyxXQUFXLENBQUMsYUFBYSxDQUFDLENBQUM7QUFDcEcsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIElBTSBTdGF0ZW1lbnQgbWVyZ2luZ1xuLy9cbi8vIFNlZSBkb2NzL3BvbGljeS1tZXJnaW5nLmFscyBmb3IgYSBmb3JtYWwgbW9kZWwgb2YgdGhlIGxvZ2ljXG4vLyBpbXBsZW1lbnRlZCBoZXJlLlxuXG5cbmltcG9ydCB7IElDb25zdHJ1Y3QgfSBmcm9tICdAYXdzLWNkay9jb3JlJztcbmltcG9ydCB7IFBvbGljeVN0YXRlbWVudCwgRXN0aW1hdGVTaXplT3B0aW9ucywgZGVyaXZlRXN0aW1hdGVTaXplT3B0aW9ucyB9IGZyb20gJy4uL3BvbGljeS1zdGF0ZW1lbnQnO1xuaW1wb3J0IHsgSVByaW5jaXBhbCB9IGZyb20gJy4uL3ByaW5jaXBhbHMnO1xuaW1wb3J0IHsgTElURVJBTF9TVFJJTkdfS0VZIH0gZnJvbSAnLi4vdXRpbCc7XG5pbXBvcnQgeyBwYXJ0aXRpb25QcmluY2lwYWxzIH0gZnJvbSAnLi9jb21wYXJhYmxlLXByaW5jaXBhbCc7XG5cblxuLypcbiAqIERvbid0IHByb2R1Y2UgYW55IG1lcmdlZCBzdGF0ZW1lbnRzIGxhcmdlciB0aGFuIHRoaXMuXG4gKlxuICogVGhleSB3aWxsIGJlY29tZSBpbXBvc3NpYmxlIHRvIGRpdmlkZSBhY3Jvc3MgbWFuYWdlZCBwb2xpY2llcyBpZiB3ZSBkbyxcbiAqIGFuZCB0aGlzIGlzIHRoZSBtYXhpbXVtIHNpemUgZm9yIFVzZXIgcG9saWNpZXMuXG4gKi9cbmNvbnN0IE1BWF9NRVJHRV9TSVpFID0gMjAwMDtcblxuLyoqXG4gKiBNZXJnZSBhcyBtYW55IHN0YXRlbWVudHMgYXMgcG9zc2libGUgdG8gc2hyaW5rIHRoZSB0b3RhbCBwb2xpY3kgZG9jLCBtb2RpZnlpbmcgdGhlIGlucHV0IGFycmF5IGluIHBsYWNlXG4gKlxuICogV2UgY29tcGFyZSBhbmQgbWVyZ2UgYWxsIHBhaXJzIG9mIHN0YXRlbWVudHMgKE8oTl4yKSBjb21wbGV4aXR5KSwgb3Bwb3J0dW5pc3RpY2FsbHlcbiAqIG1lcmdpbmcgdGhlbS4gVGhpcyBpcyBub3QgZ3VhcmFudGVlZCB0byBwcm9kdWNlIHRoZSBvcHRpbWFsIG91dHB1dCwgYnV0IGl0J3MgcHJvYmFibHlcbiAqIEdvb2QgRW5vdWdoKHRtKS4gSWYgaXQgbWVyZ2VzIGFueXRoaW5nLCBpdCdzIGF0IGxlYXN0IGdvaW5nIHRvIHByb2R1Y2UgYSBzbWFsbGVyIG91dHB1dFxuICogdGhhbiB0aGUgaW5wdXQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtZXJnZVN0YXRlbWVudHMoc2NvcGU6IElDb25zdHJ1Y3QsIHN0YXRlbWVudHM6IFBvbGljeVN0YXRlbWVudFtdLCBsaW1pdFNpemU6IGJvb2xlYW4pOiBNZXJnZVN0YXRlbWVudFJlc3VsdCB7XG4gIGNvbnN0IHNpemVPcHRpb25zID0gZGVyaXZlRXN0aW1hdGVTaXplT3B0aW9ucyhzY29wZSk7XG4gIGNvbnN0IGNvbXBTdGF0ZW1lbnRzID0gc3RhdGVtZW50cy5tYXAobWFrZUNvbXBhcmFibGUpO1xuXG4gIC8vIEtlZXAgdHJ5aW5nIHVudGlsIG5vdGhpbmcgY2hhbmdlcyBhbnltb3JlXG4gIHdoaWxlIChvbmVQYXNzKCkpIHsgLyogYWdhaW4gKi8gfVxuXG4gIGNvbnN0IG1lcmdlZFN0YXRlbWVudHMgPSBuZXcgQXJyYXk8UG9saWN5U3RhdGVtZW50PigpO1xuICBjb25zdCBvcmlnaW5zTWFwID0gbmV3IE1hcDxQb2xpY3lTdGF0ZW1lbnQsIFBvbGljeVN0YXRlbWVudFtdPigpO1xuICBmb3IgKGNvbnN0IGNvbXAgb2YgY29tcFN0YXRlbWVudHMpIHtcbiAgICBjb25zdCBzdGF0ZW1lbnQgPSByZW5kZXJDb21wYXJhYmxlKGNvbXApO1xuICAgIG1lcmdlZFN0YXRlbWVudHMucHVzaChzdGF0ZW1lbnQpO1xuICAgIG9yaWdpbnNNYXAuc2V0KHN0YXRlbWVudCwgY29tcC5vcmlnaW5hbHMpO1xuICB9XG5cbiAgcmV0dXJuIHsgbWVyZ2VkU3RhdGVtZW50cywgb3JpZ2luc01hcCB9O1xuXG4gIC8vIERvIG9uZSBvcHRpbWl6YXRpb24gcGFzcywgcmV0dXJuICd0cnVlJyBpZiB3ZSBtZXJnZWQgYW55dGhpbmdcbiAgZnVuY3Rpb24gb25lUGFzcygpIHtcbiAgICBsZXQgcmV0ID0gZmFsc2U7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNvbXBTdGF0ZW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBsZXQgaiA9IGkgKyAxO1xuICAgICAgd2hpbGUgKGogPCBjb21wU3RhdGVtZW50cy5sZW5ndGgpIHtcbiAgICAgICAgY29uc3QgbWVyZ2VkID0gdHJ5TWVyZ2UoY29tcFN0YXRlbWVudHNbaV0sIGNvbXBTdGF0ZW1lbnRzW2pdLCBsaW1pdFNpemUsIHNpemVPcHRpb25zKTtcblxuICAgICAgICBpZiAobWVyZ2VkKSB7XG4gICAgICAgICAgY29tcFN0YXRlbWVudHNbaV0gPSBtZXJnZWQ7XG4gICAgICAgICAgY29tcFN0YXRlbWVudHMuc3BsaWNlKGosIDEpO1xuICAgICAgICAgIHJldCA9IHRydWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaisrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHJldDtcbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1lcmdlU3RhdGVtZW50UmVzdWx0IHtcbiAgLyoqXG4gICAqIFRoZSBsaXN0IG9mIG1heGltYWxseSBtZXJnZWQgc3RhdGVtZW50c1xuICAgKi9cbiAgcmVhZG9ubHkgbWVyZ2VkU3RhdGVtZW50czogUG9saWN5U3RhdGVtZW50W107XG5cbiAgLyoqXG4gICAqIE1hcHBpbmcgb2Ygb2xkIHRvIG5ldyBzdGF0ZW1lbnRzXG4gICAqL1xuICByZWFkb25seSBvcmlnaW5zTWFwOiBNYXA8UG9saWN5U3RhdGVtZW50LCBQb2xpY3lTdGF0ZW1lbnRbXT47XG59XG5cbi8qKlxuICogR2l2ZW4gdHdvIHN0YXRlbWVudHMsIHJldHVybiB0aGVpciBtZXJnaW5nIChpZiBwb3NzaWJsZSlcbiAqXG4gKiBXZSBjYW4gbWVyZ2UgdHdvIHN0YXRlbWVudHMgaWY6XG4gKlxuICogLSBUaGVpciBlZmZlY3RzIGFyZSB0aGUgc2FtZVxuICogLSBUaGV5IGRvbid0IGhhdmUgU2lkcyAobm90IHJlYWxseSBhIGhhcmQgcmVxdWlyZW1lbnQsIGJ1dCBqdXN0IGEgc2ltcGxpZmljYXRpb24gYW5kIGFuIGVzY2FwZSBoYXRjaClcbiAqIC0gVGhlaXIgQ29uZGl0aW9ucyBhcmUgdGhlIHNhbWVcbiAqIC0gVGhlaXIgTm90QWN0aW9uLCBOb3RSZXNvdXJjZSBhbmQgTm90UHJpbmNpcGFsIHNldHMgYXJlIHRoZSBzYW1lIChlbXB0eSBzZXRzIGlzIGZpbmUpLlxuICogLSBGcm9tIHRoZWlyIEFjdGlvbiwgUmVzb3VyY2UgYW5kIFByaW5jaXBhbCBzZXRzLCAyIGFyZSBzdWJzZXRzIG9mIGVhY2ggb3RoZXJcbiAqICAgKGVtcHR5IHNldHMgYXJlIGZpbmUpLlxuICovXG5mdW5jdGlvbiB0cnlNZXJnZShhOiBDb21wYXJhYmxlU3RhdGVtZW50LCBiOiBDb21wYXJhYmxlU3RhdGVtZW50LCBsaW1pdFNpemU6IGJvb2xlYW4sIG9wdGlvbnM6IEVzdGltYXRlU2l6ZU9wdGlvbnMpOiBDb21wYXJhYmxlU3RhdGVtZW50IHwgdW5kZWZpbmVkIHtcbiAgLy8gRWZmZWN0cyBtdXN0IGJlIHRoZSBzYW1lXG4gIGlmIChhLnN0YXRlbWVudC5lZmZlY3QgIT09IGIuc3RhdGVtZW50LmVmZmVjdCkgeyByZXR1cm47IH1cbiAgLy8gV2UgZG9uJ3QgbWVyZ2UgU2lkcyAoZm9yIG5vdylcbiAgaWYgKGEuc3RhdGVtZW50LnNpZCB8fCBiLnN0YXRlbWVudC5zaWQpIHsgcmV0dXJuOyB9XG5cbiAgaWYgKGEuY29uZGl0aW9uU3RyaW5nICE9PSBiLmNvbmRpdGlvblN0cmluZykgeyByZXR1cm47IH1cbiAgaWYgKFxuICAgICFzZXRFcXVhbChhLnN0YXRlbWVudC5ub3RBY3Rpb25zLCBiLnN0YXRlbWVudC5ub3RBY3Rpb25zKSB8fFxuICAgICFzZXRFcXVhbChhLnN0YXRlbWVudC5ub3RSZXNvdXJjZXMsIGIuc3RhdGVtZW50Lm5vdFJlc291cmNlcykgfHxcbiAgICAhc2V0RXF1YWxQcmluY2lwYWxzKGEuc3RhdGVtZW50Lm5vdFByaW5jaXBhbHMsIGIuc3RhdGVtZW50Lm5vdFByaW5jaXBhbHMpXG4gICkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIFdlIGNhbiBtZXJnZSB0aGVzZSBzdGF0ZW1lbnRzIGlmIDIgb3V0IG9mIHRoZSAzIHNldHMgb2YgQWN0aW9uLCBSZXNvdXJjZSwgUHJpbmNpcGFsXG4gIC8vIGFyZSB0aGUgc2FtZS5cbiAgY29uc3Qgc2V0c0VxdWFsID0gKHNldEVxdWFsKGEuc3RhdGVtZW50LmFjdGlvbnMsIGIuc3RhdGVtZW50LmFjdGlvbnMpID8gMSA6IDApICtcbiAgICAoc2V0RXF1YWwoYS5zdGF0ZW1lbnQucmVzb3VyY2VzLCBiLnN0YXRlbWVudC5yZXNvdXJjZXMpID8gMSA6IDApICtcbiAgICAoc2V0RXF1YWxQcmluY2lwYWxzKGEuc3RhdGVtZW50LnByaW5jaXBhbHMsIGIuc3RhdGVtZW50LnByaW5jaXBhbHMpID8gMSA6IDApO1xuXG4gIGlmIChzZXRzRXF1YWwgPCAyIHx8IHVubWVyZ2VhYmxlUHJpbmNpcGFscyhhLCBiKSkgeyByZXR1cm47IH1cblxuICBjb25zdCBjb21iaW5lZCA9IGEuc3RhdGVtZW50LmNvcHkoe1xuICAgIGFjdGlvbnM6IHNldE1lcmdlKGEuc3RhdGVtZW50LmFjdGlvbnMsIGIuc3RhdGVtZW50LmFjdGlvbnMpLFxuICAgIHJlc291cmNlczogc2V0TWVyZ2UoYS5zdGF0ZW1lbnQucmVzb3VyY2VzLCBiLnN0YXRlbWVudC5yZXNvdXJjZXMpLFxuICAgIHByaW5jaXBhbHM6IHNldE1lcmdlUHJpbmNpcGFscyhhLnN0YXRlbWVudC5wcmluY2lwYWxzLCBiLnN0YXRlbWVudC5wcmluY2lwYWxzKSxcbiAgfSk7XG5cbiAgaWYgKGxpbWl0U2l6ZSAmJiBjb21iaW5lZC5fZXN0aW1hdGVTaXplKG9wdGlvbnMpID4gTUFYX01FUkdFX1NJWkUpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuXG4gIHJldHVybiB7XG4gICAgb3JpZ2luYWxzOiBbLi4uYS5vcmlnaW5hbHMsIC4uLmIub3JpZ2luYWxzXSxcbiAgICBzdGF0ZW1lbnQ6IGNvbWJpbmVkLFxuICAgIGNvbmRpdGlvblN0cmluZzogYS5jb25kaXRpb25TdHJpbmcsXG4gIH07XG59XG5cbi8qKlxuICogQ2FsY3VsYXRlIGFuZCByZXR1cm4gY2FjaGVkIHN0cmluZyBzZXQgcmVwcmVzZW50YXRpb24gb2YgdGhlIHN0YXRlbWVudCBlbGVtZW50c1xuICpcbiAqIFRoaXMgaXMgdG8gYmUgYWJsZSB0byBkbyBjb21wYXJpc29ucyBvbiB0aGVzZSBzZXRzIHF1aWNrbHkuXG4gKi9cbmZ1bmN0aW9uIG1ha2VDb21wYXJhYmxlKHM6IFBvbGljeVN0YXRlbWVudCk6IENvbXBhcmFibGVTdGF0ZW1lbnQge1xuICByZXR1cm4ge1xuICAgIG9yaWdpbmFsczogW3NdLFxuICAgIHN0YXRlbWVudDogcyxcbiAgICBjb25kaXRpb25TdHJpbmc6IEpTT04uc3RyaW5naWZ5KHMuY29uZGl0aW9ucyksXG4gIH07XG59XG5cbi8qKlxuICogUmV0dXJuICd0cnVlJyBpZiB0aGUgdHdvIHByaW5jaXBhbHMgYXJlIHVubWVyZ2VhYmxlXG4gKlxuICogVGhpcyBvbmx5IGhhcHBlbnMgaWYgb25lIG9mIHRoZW0gaXMgYSBsaXRlcmFsLCB1bnR5cGVkIHByaW5jaXBhbCAodHlwaWNhbGx5LFxuICogYFByaW5jaXBhbDogJyonYCkgYW5kIHRoZSBvdGhlciBvbmUgaXMgdHlwZWQuXG4gKlxuICogYFByaW5jaXBhbDogJyonYCBiZWhhdmVzIHN1YnRseSBkaWZmZXJlbnQgdGhhbiBgUHJpbmNpcGFsOiB7IEFXUzogJyonIH1gIGFuZCBtdXN0XG4gKiB0aGVyZWZvcmUgYmUgcHJlc2VydmVkLlxuICovXG5mdW5jdGlvbiB1bm1lcmdlYWJsZVByaW5jaXBhbHMoYTogQ29tcGFyYWJsZVN0YXRlbWVudCwgYjogQ29tcGFyYWJsZVN0YXRlbWVudCkge1xuICBjb25zdCBhSGFzTGl0ZXJhbCA9IGEuc3RhdGVtZW50LnByaW5jaXBhbHMuc29tZSh2ID0+IExJVEVSQUxfU1RSSU5HX0tFWSBpbiB2LnBvbGljeUZyYWdtZW50LnByaW5jaXBhbEpzb24pO1xuICBjb25zdCBiSGFzTGl0ZXJhbCA9IGIuc3RhdGVtZW50LnByaW5jaXBhbHMuc29tZSh2ID0+IExJVEVSQUxfU1RSSU5HX0tFWSBpbiB2LnBvbGljeUZyYWdtZW50LnByaW5jaXBhbEpzb24pO1xuICByZXR1cm4gYUhhc0xpdGVyYWwgIT09IGJIYXNMaXRlcmFsO1xufVxuXG4vKipcbiAqIFR1cm4gYSBDb21wYXJhYmxlU3RhdGVtZW50IGJhY2sgaW50byBhIFN0YXRlbWVudFxuICovXG5mdW5jdGlvbiByZW5kZXJDb21wYXJhYmxlKHM6IENvbXBhcmFibGVTdGF0ZW1lbnQpOiBQb2xpY3lTdGF0ZW1lbnQge1xuICByZXR1cm4gcy5zdGF0ZW1lbnQ7XG59XG5cbi8qKlxuICogQW4gYW5hbHl6ZWQgdmVyc2lvbiBvZiBhIHN0YXRlbWVudCB0aGF0IG1ha2VzIGl0IGVhc2llciB0byBkbyBjb21wYXJpc29ucyBhbmQgbWVyZ2luZyBvblxuICovXG5pbnRlcmZhY2UgQ29tcGFyYWJsZVN0YXRlbWVudCB7XG4gIHJlYWRvbmx5IHN0YXRlbWVudDogUG9saWN5U3RhdGVtZW50O1xuICByZWFkb25seSBvcmlnaW5hbHM6IFBvbGljeVN0YXRlbWVudFtdO1xuICByZWFkb25seSBjb25kaXRpb25TdHJpbmc6IHN0cmluZztcbn1cblxuLyoqXG4gKiBXaGV0aGVyIHRoZSBnaXZlbiBzZXRzIGFyZSBlcXVhbFxuICovXG5mdW5jdGlvbiBzZXRFcXVhbDxBPihhOiBBW10sIGI6IEFbXSkge1xuICBjb25zdCBiU2V0ID0gbmV3IFNldChiKTtcbiAgcmV0dXJuIGEubGVuZ3RoID09PSBiLmxlbmd0aCAmJiBhLmV2ZXJ5KGsgPT4gYlNldC5oYXMoaykpO1xufVxuXG4vKipcbiAqIE1lcmdlIHR3byB2YWx1ZSBzZXRzXG4gKi9cbmZ1bmN0aW9uIHNldE1lcmdlPEE+KHg6IEFbXSwgeTogQVtdKTogQVtdIHtcbiAgcmV0dXJuIEFycmF5LmZyb20obmV3IFNldChbLi4ueCwgLi4ueV0pKS5zb3J0KCk7XG59XG5cbmZ1bmN0aW9uIHNldEVxdWFsUHJpbmNpcGFscyh4czogSVByaW5jaXBhbFtdLCB5czogSVByaW5jaXBhbFtdKTogYm9vbGVhbiB7XG4gIGNvbnN0IHhQcmluY2lwYWxzID0gcGFydGl0aW9uUHJpbmNpcGFscyh4cyk7XG4gIGNvbnN0IHlQcmluY2lwYWxzID0gcGFydGl0aW9uUHJpbmNpcGFscyh5cyk7XG5cbiAgY29uc3Qgbm9uQ29tcCA9IHNldEVxdWFsKHhQcmluY2lwYWxzLm5vbkNvbXBhcmFibGUsIHlQcmluY2lwYWxzLm5vbkNvbXBhcmFibGUpO1xuICBjb25zdCBjb21wID0gc2V0RXF1YWwoT2JqZWN0LmtleXMoeFByaW5jaXBhbHMuY29tcGFyYWJsZSksIE9iamVjdC5rZXlzKHlQcmluY2lwYWxzLmNvbXBhcmFibGUpKTtcblxuICByZXR1cm4gbm9uQ29tcCAmJiBjb21wO1xufVxuXG5mdW5jdGlvbiBzZXRNZXJnZVByaW5jaXBhbHMoeHM6IElQcmluY2lwYWxbXSwgeXM6IElQcmluY2lwYWxbXSk6IElQcmluY2lwYWxbXSB7XG4gIGNvbnN0IHhQcmluY2lwYWxzID0gcGFydGl0aW9uUHJpbmNpcGFscyh4cyk7XG4gIGNvbnN0IHlQcmluY2lwYWxzID0gcGFydGl0aW9uUHJpbmNpcGFscyh5cyk7XG5cbiAgY29uc3QgY29tcGFyYWJsZSA9IHsgLi4ueFByaW5jaXBhbHMuY29tcGFyYWJsZSwgLi4ueVByaW5jaXBhbHMuY29tcGFyYWJsZSB9O1xuICByZXR1cm4gWy4uLk9iamVjdC52YWx1ZXMoY29tcGFyYWJsZSksIC4uLnhQcmluY2lwYWxzLm5vbkNvbXBhcmFibGUsIC4uLnlQcmluY2lwYWxzLm5vbkNvbXBhcmFibGVdO1xufVxuIl19
;