@k9securityio/k9-cdk
Version:
Provision strong AWS security policies easily using the AWS CDK.
254 lines • 38 kB
JavaScript
;
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.K9PolicyFactory = exports.toPascalCase = exports.canPrincipalsManageResources = exports.getAccessCapabilityFromValue = exports.AccessCapability = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const aws_iam_1 = require("aws-cdk-lib/aws-iam");
var AccessCapability;
(function (AccessCapability) {
AccessCapability["ADMINISTER_RESOURCE"] = "administer-resource";
AccessCapability["READ_CONFIG"] = "read-config";
AccessCapability["READ_DATA"] = "read-data";
AccessCapability["WRITE_DATA"] = "write-data";
AccessCapability["DELETE_DATA"] = "delete-data";
})(AccessCapability || (exports.AccessCapability = AccessCapability = {}));
function getAccessCapabilityFromValue(accessCapabilityStr) {
//https://blog.logrocket.com/typescript-string-enums-guide/
for (let key of Object.keys(AccessCapability)) {
// @ts-ignore
if (AccessCapability[key] == accessCapabilityStr) {
// https://stackoverflow.com/questions/17380845/how-do-i-convert-a-string-to-enum-in-typescript
let typedKey = key;
return AccessCapability[typedKey];
}
}
throw Error(`Could not get AccessCapability from value: ${accessCapabilityStr}`);
}
exports.getAccessCapabilityFromValue = getAccessCapabilityFromValue;
/**
* Check whether the provided access specs ensure that at least one principal can both read and administer configuration.
* @param accessSpecsByCapability is a map of access specs keyed by access capability
*
* @return true when at least one principal that can administer and read configuration exists
*/
function canPrincipalsManageResources(accessSpecsByCapability) {
let adminSpec = accessSpecsByCapability.get(AccessCapability.ADMINISTER_RESOURCE);
let readConfigSpec = accessSpecsByCapability.get(AccessCapability.READ_CONFIG);
if ((adminSpec?.allowPrincipalArns && adminSpec.allowPrincipalArns.length > 0)
&& (readConfigSpec?.allowPrincipalArns && readConfigSpec.allowPrincipalArns.length > 0)) {
const adminPrincipals = new Set(adminSpec.allowPrincipalArns);
const readConfigPrincipals = new Set(readConfigSpec.allowPrincipalArns);
const intersection = new Set([...adminPrincipals].filter(x => readConfigPrincipals.has(x)));
return intersection.size > 0;
}
return false;
}
exports.canPrincipalsManageResources = canPrincipalsManageResources;
/**
* Converts a string to PascalCase, which is useful for e.g. policy types that don't
* do not support spaces or hyphens in statement ids.
*
* @param input
*/
function toPascalCase(input) {
// Remove placeholders like ${something} and trim whitespace
const cleanedInput = input.replace(/\$\{.*?\}/g, '').trim();
// Split the input into words based on spaces, hyphens, underscores, or other delimiters
const words = cleanedInput.split(/[\s_\-]+/);
// Convert each word to PascalCase
return words
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join('');
}
exports.toPascalCase = toPascalCase;
class K9PolicyFactory {
constructor() {
/** @internal */
this._SUPPORTED_SERVICES = new Set([
'S3',
'KMS',
'DynamoDB',
'SQS',
]);
/** @internal */
this._K9CapabilityMapJSON = require('../resources/capability_summary.json'); // eslint-disable-line @typescript-eslint/no-require-imports
/** @internal */
this._K9CapabilityMapByService = new Map(Object.entries(this._K9CapabilityMapJSON));
}
/**
* Deduplicate an array of principals while preserving original order of principals.
* Note that principals may contain either strings or objects, so naive array sorting
* produces unstable results.
*
* @param principals
*/
static deduplicatePrincipals(principals) {
const observedPrincipals = new Set();
const uniquePrincipals = new Array();
for (let principal of principals) {
if (!observedPrincipals.has(principal)) {
uniquePrincipals.push(principal);
observedPrincipals.add(principal);
}
}
return uniquePrincipals;
}
getActions(service, accessCapability) {
if (!this._SUPPORTED_SERVICES.has(service) && this._K9CapabilityMapByService.has(service)) {
throw Error(`unsupported service: ${service}`);
}
let serviceCapabilitiesObj = this._K9CapabilityMapByService.get(service) || {};
let serviceCapabilitiesMap = new Map(Object.entries(serviceCapabilitiesObj));
let accessCapabilityName = accessCapability.toString();
if (serviceCapabilitiesMap &&
serviceCapabilitiesMap.has(accessCapabilityName)) {
return serviceCapabilitiesMap.get(accessCapabilityName) || Array();
}
else {
return new Array();
}
}
/** @internal */
_mergeAccessSpecs(target, addition) {
target.allowPrincipalArns.push(...addition.allowPrincipalArns);
if (target.test) {
//ok, user has specified a test at some point; ensure this desiredAccessSpec.test matches
if (target.test != addition.test) {
let msg = 'Cannot merge AccessSpecs; test attributes do not match:' +
`\n${JSON.stringify(target)}\n${JSON.stringify(addition)}`;
throw Error(msg);
}
}
else {
//first explicit test preference wins
if (addition.test) {
target.test = addition.test;
}
}
}
mergeDesiredAccessSpecsByCapability(supportedCapabilities, desiredAccess) {
let accessSpecsByCapability = new Map();
// 1. populate accessSpecsByCapability with fresh AccessSpecs for each supported capability
// 2. iterate through desiredAccess specs and merge data into what we'll use
// important: detect mismatched test types
// we can leave `test` unset in the default access specs
// and copy the value from the spec being merged if it is set
// throw Error on mismatch
// 3. generate an Allow statement for each supported capability
for (let supportedCapability of supportedCapabilities) {
//generate a default access spec for each of the service's supported capabilities
let effectiveAccessSpec = {
accessCapabilities: supportedCapability,
allowPrincipalArns: new Array(),
// leave 'test' property unset; will populate from user-provided data
};
accessSpecsByCapability.set(supportedCapability, effectiveAccessSpec);
//now... merge in the user's desired access for this capability
for (let desiredAccessSpec of desiredAccess) {
if (desiredAccessSpec.accessCapabilities instanceof Array) {
for (let desiredCapability of desiredAccessSpec.accessCapabilities) {
if (supportedCapability == desiredCapability) {
this._mergeAccessSpecs(effectiveAccessSpec, desiredAccessSpec);
}
}
}
else if (typeof desiredAccessSpec.accessCapabilities == 'string') {
if (supportedCapability == desiredAccessSpec.accessCapabilities) {
this._mergeAccessSpecs(effectiveAccessSpec, desiredAccessSpec);
}
}
else {
throw Error(`Unhandled type of accessCapabilities for ${desiredAccessSpec.accessCapabilities}`);
}
}
}
const records = {};
accessSpecsByCapability.forEach(function (value, key) {
records[key] = value;
});
return records;
}
makeAllowStatements(serviceName, supportedCapabilities, desiredAccess, resourceArns, usePascalCase = false) {
let policyStatements = new Array();
let accessSpecsByCapabilityRecs = this.mergeDesiredAccessSpecsByCapability(supportedCapabilities, desiredAccess);
let accessSpecsByCapability = new Map();
for (let [capabilityStr, accessSpec] of Object.entries(accessSpecsByCapabilityRecs)) {
accessSpecsByCapability.set(getAccessCapabilityFromValue(capabilityStr), accessSpec);
}
// ok, time to actually make Allow Statements from our AccessSpecs
for (let supportedCapability of supportedCapabilities) {
let accessSpec = accessSpecsByCapability.get(supportedCapability) ||
{
//generate a default access spec if none was provided
accessCapabilities: [supportedCapability],
allowPrincipalArns: new Array(),
test: 'ArnEquals',
};
let arnConditionTest = accessSpec.test || 'ArnEquals';
let sid = `Allow Restricted ${supportedCapability}`;
if (usePascalCase) {
sid = toPascalCase(sid);
}
let statement = this.makeAllowStatement(sid, this.getActions(serviceName, supportedCapability), accessSpec.allowPrincipalArns, arnConditionTest, resourceArns);
policyStatements.push(statement);
}
return policyStatements;
}
makeAllowStatement(sid, actions, principalArns, test, resources) {
const policyStatementProps = {
sid: sid,
effect: aws_iam_1.Effect.ALLOW,
};
const statement = new aws_iam_1.PolicyStatement(policyStatementProps);
statement.addActions(...actions);
statement.addAnyPrincipal();
statement.addResources(...resources);
statement.addCondition(test, { 'aws:PrincipalArn': K9PolicyFactory.deduplicatePrincipals(principalArns) });
return statement;
}
wasLikeUsed(accessSpecs) {
for (let accessSpec of accessSpecs) {
if ('ArnLike' == accessSpec.test) {
return true;
}
}
return false;
}
getAllowedPrincipalArns(accessSpecs) {
let allowedPrincipalArns = new Set();
for (let accessSpec of accessSpecs) {
accessSpec.allowPrincipalArns.forEach(function (value) {
allowedPrincipalArns.add(value);
});
}
return Array.from(allowedPrincipalArns);
}
/**
* k9 wants to deny all AWS accounts and IAM principals not explicitly allowed; this *should*
* be straightforward, but it isn't because of the way aws-cdk merges and manipulates Principals.
* @return list of principals for a DenyEveryoneElse statement
*/
makeDenyEveryoneElsePrincipals() {
/**
* We should be able to provide AnyPrincipal once (of course), but AWS CDK converts:
* "Principal": {
* "AWS": "*" // identifies all AWS accounts and IAM.
* }
* to:
* "Principal": "*" // identifies all principals including AWS Service principals
*
* That's a greater scope than we want.
*
* So provide AnyPrincipal twice, so aws-cdk maintains the array form.
*
* AWS rewrites the AWS member of the policy on save so
* only the unique set of principals are included
* So after these machinations, we end up with what we want.
*/
return [new aws_iam_1.AnyPrincipal(), new aws_iam_1.AnyPrincipal()];
}
}
exports.K9PolicyFactory = K9PolicyFactory;
_a = JSII_RTTI_SYMBOL_1;
K9PolicyFactory[_a] = { fqn: "@k9securityio/k9-cdk.k9policy.K9PolicyFactory", version: "2.1.3" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiazlwb2xpY3kuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvazlwb2xpY3kudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxpREFPNkI7QUFVN0IsSUFBWSxnQkFNWDtBQU5ELFdBQVksZ0JBQWdCO0lBQzFCLCtEQUEyQyxDQUFBO0lBQzNDLCtDQUEyQixDQUFBO0lBQzNCLDJDQUF1QixDQUFBO0lBQ3ZCLDZDQUF5QixDQUFBO0lBQ3pCLCtDQUEyQixDQUFBO0FBQzdCLENBQUMsRUFOVyxnQkFBZ0IsZ0NBQWhCLGdCQUFnQixRQU0zQjtBQUVELFNBQWdCLDRCQUE0QixDQUFDLG1CQUEyQjtJQUN0RSwyREFBMkQ7SUFDM0QsS0FBSyxJQUFJLEdBQUcsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsQ0FBQztRQUM5QyxhQUFhO1FBQ2IsSUFBSSxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsSUFBSSxtQkFBbUIsRUFBRSxDQUFDO1lBQ2pELCtGQUErRjtZQUMvRixJQUFJLFFBQVEsR0FBa0MsR0FBRyxDQUFDO1lBQ2xELE9BQU8sZ0JBQWdCLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDcEMsQ0FBQztJQUNILENBQUM7SUFFRCxNQUFNLEtBQUssQ0FBQyw4Q0FBOEMsbUJBQW1CLEVBQUUsQ0FBQyxDQUFDO0FBQ25GLENBQUM7QUFaRCxvRUFZQztBQTBCRDs7Ozs7R0FLRztBQUNILFNBQWdCLDRCQUE0QixDQUFDLHVCQUEyRDtJQUN0RyxJQUFJLFNBQVMsR0FBRyx1QkFBdUIsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsbUJBQW1CLENBQUMsQ0FBQztJQUNsRixJQUFJLGNBQWMsR0FBRyx1QkFBdUIsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsV0FBVyxDQUFDLENBQUM7SUFFL0UsSUFBSSxDQUFDLFNBQVMsRUFBRSxrQkFBa0IsSUFBSSxTQUFTLENBQUMsa0JBQWtCLENBQUMsTUFBTSxHQUFHLENB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QUEwQjtRQUNwQyxLQUFLLElBQUksVUFBVSxJQUFJLFdBQVcsRUFBRSxDQUFDO1lBQ25DLElBQUksU0FBUyxJQUFJLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztnQkFDakMsT0FBTyxJQUFJLENBQUM7WUFDZCxDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVELHVCQUF1QixDQUFDLFdBQTBCO1FBQ2hELElBQUksb0JBQW9CLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztRQUM3QyxLQUFLLElBQUksVUFBVSxJQUFJLFdBQVcsRUFBRSxDQUFDO1lBQ25DLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsVUFBVSxLQUFLO2dCQUNuRCxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDbEMsQ0FBQyxDQUFDLENBQUM7UUFDTCxDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUM7SUFDMUMsQ0FBQztJQUVEOzs7O1NBSUs7SUFDTCw4QkFBOEI7UUFDNUI7Ozs7Ozs7Ozs7Ozs7OztlQWVPO1FBQ1AsT0FBTyxDQUFDLElBQUksc0JBQVksRUFBRSxFQUFFLElBQUksc0JBQVksRUFBRSxDQUFDLENBQUM7SUFDbEQsQ0FBQzs7QUF6TkgsMENBMk5DIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQW55UHJpbmNpcGFsLFxuICBBcm5QcmluY2lwYWwsXG4gIENvbmRpdGlvbnMsXG4gIEVmZmVjdCxcbiAgUG9saWN5U3RhdGVtZW50LFxuICBQb2xpY3lTdGF0ZW1lbnRQcm9wcyxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWlhbSc7XG5cbmV4cG9ydCB0eXBlIEFybkVxdWFsc1Rlc3QgPSAnQXJuRXF1YWxzJ1xuXG5leHBvcnQgdHlwZSBBcm5MaWtlVGVzdCA9ICdBcm5MaWtlJztcblxuZXhwb3J0IHR5cGUgQXJuQ29uZGl0aW9uVGVzdCA9XG4gICAgfCBBcm5FcXVhbHNUZXN0XG4gICAgfCBBcm5MaWtlVGVzdDtcblxuZXhwb3J0IGVudW0gQWNjZXNzQ2FwYWJpbGl0eSB7XG4gIEFETUlOSVNURVJfUkVTT1VSQ0UgPSAnYWRtaW5pc3Rlci1yZXNvdXJjZScsXG4gIFJFQURfQ09ORklHID0gJ3JlYWQtY29uZmlnJyxcbiAgUkVBRF9EQVRBID0gJ3JlYWQtZGF0YScsXG4gIFdSSVRFX0RBVEEgPSAnd3JpdGUtZGF0YScsXG4gIERFTEVURV9EQVRBID0gJ2RlbGV0ZS1kYXRhJyxcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEFjY2Vzc0NhcGFiaWxpdHlGcm9tVmFsdWUoYWNjZXNzQ2FwYWJpbGl0eVN0cjogc3RyaW5nKTogQWNjZXNzQ2FwYWJpbGl0eSB7XG4gIC8vaHR0cHM6Ly9ibG9nLmxvZ3JvY2tldC5jb20vdHlwZXNjcmlwdC1zdHJpbmctZW51bXMtZ3VpZGUvXG4gIGZvciAobGV0IGtleSBvZiBPYmplY3Qua2V5cyhBY2Nlc3NDYXBhYmlsaXR5KSkge1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICBpZiAoQWNjZXNzQ2FwYWJpbGl0eVtrZXldID09IGFjY2Vzc0NhcGFiaWxpdHlTdHIpIHtcbiAgICAgIC8vIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzE3MzgwODQ1L2hvdy1kby1pLWNvbnZlcnQtYS1zdHJpbmctdG8tZW51bS1pbi10eXBlc2NyaXB0XG4gICAgICBsZXQgdHlwZWRLZXkgPSA8a2V5b2YgdHlwZW9mIEFjY2Vzc0NhcGFiaWxpdHk+a2V5O1xuICAgICAgcmV0dXJuIEFjY2Vzc0NhcGFiaWxpdHlbdHlwZWRLZXldO1xuICAgIH1cbiAgfVxuXG4gIHRocm93IEVycm9yKGBDb3VsZCBub3QgZ2V0IEFjY2Vzc0NhcGFiaWxpdHkgZnJvbSB2YWx1ZTogJHthY2Nlc3NDYXBhYmlsaXR5U3RyfWApO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIElBY2Nlc3NTcGVjIHtcbiAgYWNjZXNzQ2FwYWJpbGl0aWVzOiBBcnJheTxBY2Nlc3NDYXBhYmlsaXR5PiB8IEFjY2Vzc0NhcGFiaWxpdHk7XG4gIGFsbG93UHJpbmNpcGFsQXJuczogQXJyYXk8c3RyaW5nPjtcbiAgdGVzdD86IEFybkNvbmRpdGlvblRlc3Q7XG59XG5cbi8qKlxuICogYElBV1NTZXJ2aWNlQWNjZXNzR2VuZXJhdG9yYCBkZWZpbmVzIGFuIGludGVyZmFjZSB0aGF0IHRoZSBrOSBwb2xpY3kgZ2VuZXJhdG9ycyB1c2UgdG8gZ3JhbnQgYW4gQVdTIHNlcnZpY2VcbiAqIGFjY2VzcyB0byBhIHByb3RlY3RlZCByZXNvdXJjZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJQVdTU2VydmljZUFjY2Vzc0dlbmVyYXRvciB7XG4gIC8qKlxuICAgKiBNYWtlIGFuIGFycmF5IG9mIFBvbGljeVN0YXRlbWVudCBvYmplY3RzIHRoYXQgYWxsb3cgYW4gQVdTIHNlcnZpY2UsIGUuZy4gQ2xvdWRGcm9udCwgdG8gYWNjZXNzIHRvIHRoZVxuICAgKiBwcm90ZWN0ZWQgQVdTIHJlc291cmNlLlxuICAgKi9cbiAgbWFrZUFsbG93U3RhdGVtZW50cygpOiBBcnJheTxQb2xpY3lTdGF0ZW1lbnQ+O1xuXG4gIC8qKlxuICAgKiBNYWtlIGEgQ29uZGl0aW9ucyBvYmplY3QgdGhhdCBjcmVhdGVzIGFuIGV4Y2VwdGlvbiBmb3IgYW4gQVdTIHNlcnZpY2UgaW4gYSBwcm90ZWN0ZWQgcmVzb3VyY2UncyBgRGVueUV2ZXJ5b25lRWxzZWBcbiAgICogc3RhdGVtZW50LlxuICAgKi9cbiAgbWFrZUNvbmRpdGlvbnNUb0V4Y2VwdEZyb21EZW55RXZlcnlvbmVFbHNlKCk6IENvbmRpdGlvbnM7XG59XG5cbi8qKlxuICogQ2hlY2sgd2hldGhlciB0aGUgcHJvdmlkZWQgYWNjZXNzIHNwZWNzIGVuc3VyZSB0aGF0IGF0IGxlYXN0IG9uZSBwcmluY2lwYWwgY2FuIGJvdGggcmVhZCBhbmQgYWRtaW5pc3RlciBjb25maWd1cmF0aW9uLlxuICogQHBhcmFtIGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5IGlzIGEgbWFwIG9mIGFjY2VzcyBzcGVjcyBrZXllZCBieSBhY2Nlc3MgY2FwYWJpbGl0eVxuICpcbiAqIEByZXR1cm4gdHJ1ZSB3aGVuIGF0IGxlYXN0IG9uZSBwcmluY2lwYWwgdGhhdCBjYW4gYWRtaW5pc3RlciBhbmQgcmVhZCBjb25maWd1cmF0aW9uIGV4aXN0c1xuICovXG5leHBvcnQgZnVuY3Rpb24gY2FuUHJpbmNpcGFsc01hbmFnZVJlc291cmNlcyhhY2Nlc3NTcGVjc0J5Q2FwYWJpbGl0eTogTWFwPEFjY2Vzc0NhcGFiaWxpdHksIElBY2Nlc3NTcGVjPikge1xuICBsZXQgYWRtaW5TcGVjID0gYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHkuZ2V0KEFjY2Vzc0NhcGFiaWxpdHkuQURNSU5JU1RFUl9SRVNPVVJDRSk7XG4gIGxldCByZWFkQ29uZmlnU3BlYyA9IGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5LmdldChBY2Nlc3NDYXBhYmlsaXR5LlJFQURfQ09ORklHKTtcblxuICBpZiAoKGFkbWluU3BlYz8uYWxsb3dQcmluY2lwYWxBcm5zICYmIGFkbWluU3BlYy5hbGxvd1ByaW5jaXBhbEFybnMubGVuZ3RoID4gMClcbiAgICAgICAgJiYgKHJlYWRDb25maWdTcGVjPy5hbGxvd1ByaW5jaXBhbEFybnMgJiYgcmVhZENvbmZpZ1NwZWMuYWxsb3dQcmluY2lwYWxBcm5zLmxlbmd0aCA+IDApKSB7XG4gICAgY29uc3QgYWRtaW5QcmluY2lwYWxzID0gbmV3IFNldDxzdHJpbmc+KGFkbWluU3BlYy5hbGxvd1ByaW5jaXBhbEFybnMpO1xuICAgIGNvbnN0IHJlYWRDb25maWdQcmluY2lwYWxzID0gbmV3IFNldDxzdHJpbmc+KHJlYWRDb25maWdTcGVjLmFsbG93UHJpbmNpcGFsQXJucyk7XG4gICAgY29uc3QgaW50ZXJzZWN0aW9uID0gbmV3IFNldChcbiAgICAgIFsuLi5hZG1pblByaW5jaXBhbHNdLmZpbHRlcih4ID0+IHJlYWRDb25maWdQcmluY2lwYWxzLmhhcyh4KSkpO1xuICAgIHJldHVybiBpbnRlcnNlY3Rpb24uc2l6ZSA+IDA7XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG5cbi8qKlxuICogQ29udmVydHMgYSBzdHJpbmcgdG8gUGFzY2FsQ2FzZSwgd2hpY2ggaXMgdXNlZnVsIGZvciBlLmcuIHBvbGljeSB0eXBlcyB0aGF0IGRvbid0XG4gKiBkbyBub3Qgc3VwcG9ydCBzcGFjZXMgb3IgaHlwaGVucyBpbiBzdGF0ZW1lbnQgaWRzLlxuICpcbiAqIEBwYXJhbSBpbnB1dFxuICovXG5leHBvcnQgZnVuY3Rpb24gdG9QYXNjYWxDYXNlKGlucHV0OiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBSZW1vdmUgcGxhY2Vob2xkZXJzIGxpa2UgJHtzb21ldGhpbmd9IGFuZCB0cmltIHdoaXRlc3BhY2VcbiAgY29uc3QgY2xlYW5lZElucHV0ID0gaW5wdXQucmVwbGFjZSgvXFwkXFx7Lio/XFx9L2csICcnKS50cmltKCk7XG5cbiAgLy8gU3BsaXQgdGhlIGlucHV0IGludG8gd29yZHMgYmFzZWQgb24gc3BhY2VzLCBoeXBoZW5zLCB1bmRlcnNjb3Jlcywgb3Igb3RoZXIgZGVsaW1pdGVyc1xuICBjb25zdCB3b3JkcyA9IGNsZWFuZWRJbnB1dC5zcGxpdCgvW1xcc19cXC1dKy8pO1xuXG4gIC8vIENvbnZlcnQgZWFjaCB3b3JkIHRvIFBhc2NhbENhc2VcbiAgcmV0dXJuIHdvcmRzXG4gICAgLm1hcChcbiAgICAgIHdvcmQgPT4gd29yZC5jaGFyQXQoMCkudG9VcHBlckNhc2UoKSArIHdvcmQuc2xpY2UoMSkudG9Mb3dlckNhc2UoKSwgLy8gQ2FwaXRhbGl6ZSB0aGUgZmlyc3QgbGV0dGVyLCBsb3dlciB0aGUgcmVzdFxuICAgIClcbiAgICAuam9pbignJyk7XG59XG5cbmV4cG9ydCBjbGFzcyBLOVBvbGljeUZhY3Rvcnkge1xuXG4gIC8qKlxuICAgKiBEZWR1cGxpY2F0ZSBhbiBhcnJheSBvZiBwcmluY2lwYWxzIHdoaWxlIHByZXNlcnZpbmcgb3JpZ2luYWwgb3JkZXIgb2YgcHJpbmNpcGFscy5cbiAgICogTm90ZSB0aGF0IHByaW5jaXBhbHMgbWF5IGNvbnRhaW4gZWl0aGVyIHN0cmluZ3Mgb3Igb2JqZWN0cywgc28gbmFpdmUgYXJyYXkgc29ydGluZ1xuICAgKiBwcm9kdWNlcyB1bnN0YWJsZSByZXN1bHRzLlxuICAgKlxuICAgKiBAcGFyYW0gcHJpbmNpcGFsc1xuICAgKi9cbiAgc3RhdGljIGRlZHVwbGljYXRlUHJpbmNpcGFscyhwcmluY2lwYWxzOiBBcnJheTxzdHJpbmd8b2JqZWN0Pik6IEFycmF5PHN0cmluZ3xvYmplY3Q+IHtcbiAgICBjb25zdCBvYnNlcnZlZFByaW5jaXBhbHMgPSBuZXcgU2V0PHN0cmluZ3xvYmplY3Q+KCk7XG4gICAgY29uc3QgdW5pcXVlUHJpbmNpcGFscyA9IG5ldyBBcnJheTxzdHJpbmd8b2JqZWN0PigpO1xuICAgIGZvciAobGV0IHByaW5jaXBhbCBvZiBwcmluY2lwYWxzKSB7XG4gICAgICBpZiAoIW9ic2VydmVkUHJpbmNpcGFscy5oYXMocHJpbmNpcGFsKSkge1xuICAgICAgICB1bmlxdWVQcmluY2lwYWxzLnB1c2gocHJpbmNpcGFsKTtcbiAgICAgICAgb2JzZXJ2ZWRQcmluY2lwYWxzLmFkZChwcmluY2lwYWwpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdW5pcXVlUHJpbmNpcGFscztcbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgX1NVUFBPUlRFRF9TRVJWSUNFUyA9IG5ldyBTZXQ8c3RyaW5nPihbXG4gICAgJ1MzJyxcbiAgICAnS01TJyxcbiAgICAnRHluYW1vREInLFxuICAgICdTUVMnLFxuICBdKTtcblxuICAvKiogQGludGVybmFsICovXG4gIF9LOUNhcGFiaWxpdHlNYXBKU09OOiBPYmplY3QgPSByZXF1aXJlKCcuLi9yZXNvdXJjZXMvY2FwYWJpbGl0eV9zdW1tYXJ5Lmpzb24nKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgX0s5Q2FwYWJpbGl0eU1hcEJ5U2VydmljZTogTWFwPHN0cmluZywgT2JqZWN0PiA9IG5ldyBNYXAoT2JqZWN0LmVudHJpZXModGhpcy5fSzlDYXBhYmlsaXR5TWFwSlNPTikpO1xuXG4gIGdldEFjdGlvbnMoc2VydmljZTogc3RyaW5nLCBhY2Nlc3NDYXBhYmlsaXR5OiBBY2Nlc3NDYXBhYmlsaXR5KTogQXJyYXk8c3RyaW5nPiB7XG4gICAgaWYgKCF0aGlzLl9TVVBQT1JURURfU0VSVklDRVMuaGFzKHNlcnZpY2UpICYmIHRoaXMuX0s5Q2FwYWJpbGl0eU1hcEJ5U2VydmljZS5oYXMoc2VydmljZSkpIHtcbiAgICAgIHRocm93IEVycm9yKGB1bnN1cHBvcnRlZCBzZXJ2aWNlOiAke3NlcnZpY2V9YCk7XG4gICAgfVxuXG4gICAgbGV0IHNlcnZpY2VDYXBhYmlsaXRpZXNPYmo6IE9iamVjdCA9IHRoaXMuX0s5Q2FwYWJpbGl0eU1hcEJ5U2VydmljZS5nZXQoc2VydmljZSkgfHwge307XG4gICAgbGV0IHNlcnZpY2VDYXBhYmlsaXRpZXNNYXAgPSBuZXcgTWFwPHN0cmluZywgQXJyYXk8c3RyaW5nPj4oT2JqZWN0LmVudHJpZXMoc2VydmljZUNhcGFiaWxpdGllc09iaikpO1xuXG4gICAgbGV0IGFjY2Vzc0NhcGFiaWxpdHlOYW1lID0gYWNjZXNzQ2FwYWJpbGl0eS50b1N0cmluZygpO1xuICAgIGlmIChzZXJ2aWNlQ2FwYWJpbGl0aWVzTWFwICYmXG4gICAgICAgICAgICBzZXJ2aWNlQ2FwYWJpbGl0aWVzTWFwLmhhcyhhY2Nlc3NDYXBhYmlsaXR5TmFtZSkpIHtcbiAgICAgIHJldHVybiBzZXJ2aWNlQ2FwYWJpbGl0aWVzTWFwLmdldChhY2Nlc3NDYXBhYmlsaXR5TmFtZSkgfHwgQXJyYXk8c3RyaW5nPigpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbmV3IEFycmF5PHN0cmluZz4oKTtcbiAgICB9XG4gIH1cblxuICAvKiogQGludGVybmFsICovXG4gIF9tZXJnZUFjY2Vzc1NwZWNzKHRhcmdldDogSUFjY2Vzc1NwZWMsIGFkZGl0aW9uOiBJQWNjZXNzU3BlYykge1xuICAgIHRhcmdldC5hbGxvd1ByaW5jaXBhbEFybnMucHVzaCguLi5hZGRpdGlvbi5hbGxvd1ByaW5jaXBhbEFybnMpO1xuICAgIGlmICh0YXJnZXQudGVzdCkge1xuICAgICAgLy9vaywgdXNlciBoYXMgc3BlY2lmaWVkIGEgdGVzdCBhdCBzb21lIHBvaW50OyBlbnN1cmUgdGhpcyBkZXNpcmVkQWNjZXNzU3BlYy50ZXN0IG1hdGNoZXNcbiAgICAgIGlmICh0YXJnZXQudGVzdCAhPSBhZGRpdGlvbi50ZXN0KSB7XG4gICAgICAgIGxldCBtc2cgPSAnQ2Fubm90IG1lcmdlIEFjY2Vzc1NwZWNzOyB0ZXN0IGF0dHJpYnV0ZXMgZG8gbm90IG1hdGNoOicgK1xuICAgICAgICAgICAgICAgICAgICBgXFxuJHtKU09OLnN0cmluZ2lmeSh0YXJnZXQpfVxcbiR7SlNPTi5zdHJpbmdpZnkoYWRkaXRpb24pfWA7XG4gICAgICAgIHRocm93IEVycm9yKG1zZyk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vZmlyc3QgZXhwbGljaXQgdGVzdCBwcmVmZXJlbmNlIHdpbnNcbiAgICAgIGlmIChhZGRpdGlvbi50ZXN0KSB7XG4gICAgICAgIHRhcmdldC50ZXN0ID0gYWRkaXRpb24udGVzdDtcbiAgICAgIH1cbiAgICB9XG5cbiAgfVxuXG4gIG1lcmdlRGVzaXJlZEFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5KHN1cHBvcnRlZENhcGFiaWxpdGllczogQXJyYXk8QWNjZXNzQ2FwYWJpbGl0eT4sXG4gICAgZGVzaXJlZEFjY2VzczogQXJyYXk8SUFjY2Vzc1NwZWM+KTogUmVjb3JkPHN0cmluZywgSUFjY2Vzc1NwZWM+IHtcblxuICAgIGxldCBhY2Nlc3NTcGVjc0J5Q2FwYWJpbGl0eTogTWFwPEFjY2Vzc0NhcGFiaWxpdHksIElBY2Nlc3NTcGVjPiA9IG5ldyBNYXA8QWNjZXNzQ2FwYWJpbGl0eSwgSUFjY2Vzc1NwZWM+KCk7XG4gICAgLy8gMS4gcG9wdWxhdGUgYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHkgd2l0aCBmcmVzaCBBY2Nlc3NTcGVjcyBmb3IgZWFjaCBzdXBwb3J0ZWQgY2FwYWJpbGl0eVxuICAgIC8vIDIuIGl0ZXJhdGUgdGhyb3VnaCBkZXNpcmVkQWNjZXNzIHNwZWNzIGFuZCBtZXJnZSBkYXRhIGludG8gd2hhdCB3ZSdsbCB1c2VcbiAgICAvLyAgICBpbXBvcnRhbnQ6IGRldGVjdCBtaXNtYXRjaGVkIHRlc3QgdHlwZXNcbiAgICAvLyAgICAgd2UgY2FuIGxlYXZlIGB0ZXN0YCB1bnNldCBpbiB0aGUgZGVmYXVsdCBhY2Nlc3Mgc3BlY3NcbiAgICAvLyAgICAgYW5kIGNvcHkgdGhlIHZhbHVlIGZyb20gdGhlIHNwZWMgYmVpbmcgbWVyZ2VkIGlmIGl0IGlzIHNldFxuICAgIC8vICAgICB0aHJvdyBFcnJvciBvbiBtaXNtYXRjaFxuICAgIC8vIDMuIGdlbmVyYXRlIGFuIEFsbG93IHN0YXRlbWVudCBmb3IgZWFjaCBzdXBwb3J0ZWQgY2FwYWJpbGl0eVxuXG4gICAgZm9yIChsZXQgc3VwcG9ydGVkQ2FwYWJpbGl0eSBvZiBzdXBwb3J0ZWRDYXBhYmlsaXRpZXMpIHtcbiAgICAgIC8vZ2VuZXJhdGUgYSBkZWZhdWx0IGFjY2VzcyBzcGVjIGZvciBlYWNoIG9mIHRoZSBzZXJ2aWNlJ3Mgc3VwcG9ydGVkIGNhcGFiaWxpdGllc1xuICAgICAgbGV0IGVmZmVjdGl2ZUFjY2Vzc1NwZWM6IElBY2Nlc3NTcGVjID0ge1xuICAgICAgICBhY2Nlc3NDYXBhYmlsaXRpZXM6IHN1cHBvcnRlZENhcGFiaWxpdHksXG4gICAgICAgIGFsbG93UHJpbmNpcGFsQXJuczogbmV3IEFycmF5PHN0cmluZz4oKSxcbiAgICAgICAgLy8gbGVhdmUgJ3Rlc3QnIHByb3BlcnR5IHVuc2V0OyB3aWxsIHBvcHVsYXRlIGZyb20gdXNlci1wcm92aWRlZCBkYXRhXG4gICAgICB9O1xuICAgICAgYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHkuc2V0KHN1cHBvcnRlZENhcGFiaWxpdHksIGVmZmVjdGl2ZUFjY2Vzc1NwZWMpO1xuXG4gICAgICAvL25vdy4uLiBtZXJnZSBpbiB0aGUgdXNlcidzIGRlc2lyZWQgYWNjZXNzIGZvciB0aGlzIGNhcGFiaWxpdHlcbiAgICAgIGZvciAobGV0IGRlc2lyZWRBY2Nlc3NTcGVjIG9mIGRlc2lyZWRBY2Nlc3MpIHtcbiAgICAgICAgaWYgKGRlc2lyZWRBY2Nlc3NTcGVjLmFjY2Vzc0NhcGFiaWxpdGllcyBpbnN0YW5jZW9mIEFycmF5KSB7XG4gICAgICAgICAgZm9yIChsZXQgZGVzaXJlZENhcGFiaWxpdHkgb2YgZGVzaXJlZEFjY2Vzc1NwZWMuYWNjZXNzQ2FwYWJpbGl0aWVzKSB7XG4gICAgICAgICAgICBpZiAoc3VwcG9ydGVkQ2FwYWJpbGl0eSA9PSBkZXNpcmVkQ2FwYWJpbGl0eSkge1xuICAgICAgICAgICAgICB0aGlzLl9tZXJnZUFjY2Vzc1NwZWNzKGVmZmVjdGl2ZUFjY2Vzc1NwZWMsIGRlc2lyZWRBY2Nlc3NTcGVjKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGRlc2lyZWRBY2Nlc3NTcGVjLmFjY2Vzc0NhcGFiaWxpdGllcyA9PSAnc3RyaW5nJykge1xuICAgICAgICAgIGlmIChzdXBwb3J0ZWRDYXBhYmlsaXR5ID09IGRlc2lyZWRBY2Nlc3NTcGVjLmFjY2Vzc0NhcGFiaWxpdGllcykge1xuICAgICAgICAgICAgdGhpcy5fbWVyZ2VBY2Nlc3NTcGVjcyhlZmZlY3RpdmVBY2Nlc3NTcGVjLCBkZXNpcmVkQWNjZXNzU3BlYyk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRocm93IEVycm9yKGBVbmhhbmRsZWQgdHlwZSBvZiBhY2Nlc3NDYXBhYmlsaXRpZXMgZm9yICR7ZGVzaXJlZEFjY2Vzc1NwZWMuYWNjZXNzQ2FwYWJpbGl0aWVzfWApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgcmVjb3JkczogUmVjb3JkPHN0cmluZywgSUFjY2Vzc1NwZWM+ID0ge307XG4gICAgYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHkuZm9yRWFjaChmdW5jdGlvbiAodmFsdWUsIGtleSkge1xuICAgICAgcmVjb3Jkc1trZXldID0gdmFsdWU7XG4gICAgfSk7XG4gICAgcmV0dXJuIHJlY29yZHM7XG4gIH1cblxuICBtYWtlQWxsb3dTdGF0ZW1lbnRzKHNlcnZpY2VOYW1lOiBzdHJpbmcsXG4gICAgc3VwcG9ydGVkQ2FwYWJpbGl0aWVzOiBBcnJheTxBY2Nlc3NDYXBhYmlsaXR5PixcbiAgICBkZXNpcmVkQWNjZXNzOiBBcnJheTxJQWNjZXNzU3BlYz4sXG4gICAgcmVzb3VyY2VBcm5zOiBBcnJheTxzdHJpbmc+LFxuICAgIHVzZVBhc2NhbENhc2U6IGJvb2xlYW4gPSBmYWxzZSk6IEFycmF5PFBvbGljeVN0YXRlbWVudD4ge1xuICAgIGxldCBwb2xpY3lTdGF0ZW1lbnRzID0gbmV3IEFycmF5PFBvbGljeVN0YXRlbWVudD4oKTtcbiAgICBsZXQgYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHlSZWNzID0gdGhpcy5tZXJnZURlc2lyZWRBY2Nlc3NTcGVjc0J5Q2FwYWJpbGl0eShzdXBwb3J0ZWRDYXBhYmlsaXRpZXMsIGRlc2lyZWRBY2Nlc3MpO1xuICAgIGxldCBhY2Nlc3NTcGVjc0J5Q2FwYWJpbGl0eTogTWFwPEFjY2Vzc0NhcGFiaWxpdHksIElBY2Nlc3NTcGVjPiA9IG5ldyBNYXAoKTtcblxuICAgIGZvciAobGV0IFtjYXBhYmlsaXR5U3RyLCBhY2Nlc3NTcGVjXSBvZiBPYmplY3QuZW50cmllcyhhY2Nlc3NTcGVjc0J5Q2FwYWJpbGl0eVJlY3MpKSB7XG4gICAgICBhY2Nlc3NTcGVjc0J5Q2FwYWJpbGl0eS5zZXQoZ2V0QWNjZXNzQ2FwYWJpbGl0eUZyb21WYWx1ZShjYXBhYmlsaXR5U3RyKSwgYWNjZXNzU3BlYyk7XG4gICAgfVxuXG4gICAgLy8gb2ssIHRpbWUgdG8gYWN0dWFsbHkgbWFrZSBBbGxvdyBTdGF0ZW1lbnRzIGZyb20gb3VyIEFjY2Vzc1NwZWNzXG4gICAgZm9yIChsZXQgc3VwcG9ydGVkQ2FwYWJpbGl0eSBvZiBzdXBwb3J0ZWRDYXBhYmlsaXRpZXMpIHtcblxuICAgICAgbGV0IGFjY2Vzc1NwZWM6IElBY2Nlc3NTcGVjID0gYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHkuZ2V0KHN1cHBvcnRlZENhcGFiaWxpdHkpIHx8XG4gICAgICAgICAgICAgICAgeyAvL3NhdGlzZnkgY29tcGlsZXI7IHNob3VsZCBuZXZlciBoYXBwZW4sIGJlY2F1c2Ugd2UgcG9wdWxhdGUgYXQgdGhlIGJlZ2lubmluZy5cbiAgICAgICAgICAgICAgICAgIC8vZ2VuZXJhdGUgYSBkZWZhdWx0IGFjY2VzcyBzcGVjIGlmIG5vbmUgd2FzIHByb3ZpZGVkXG4gICAgICAgICAgICAgICAgICBhY2Nlc3NDYXBhYmlsaXRpZXM6IFtzdXBwb3J0ZWRDYXBhYmlsaXR5XSxcbiAgICAgICAgICAgICAgICAgIGFsbG93UHJpbmNpcGFsQXJuczogbmV3IEFycmF5PHN0cmluZz4oKSxcbiAgICAgICAgICAgICAgICAgIHRlc3Q6ICdBcm5FcXVhbHMnLFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIDtcblxuICAgICAgbGV0IGFybkNvbmRpdGlvblRlc3QgPSBhY2Nlc3NTcGVjLnRlc3QgfHwgJ0FybkVxdWFscyc7XG5cbiAgICAgIGxldCBzaWQgPSBgQWxsb3cgUmVzdHJpY3RlZCAke3N1cHBvcnRlZENhcGFiaWxpdHl9YDtcbiAgICAgIGlmICh1c2VQYXNjYWxDYXNlKSB7XG4gICAgICAgIHNpZCA9IHRvUGFzY2FsQ2FzZShzaWQpO1xuICAgICAgfVxuXG4gICAgICBsZXQgc3RhdGVtZW50ID0gdGhpcy5tYWtlQWxsb3dTdGF0ZW1lbnQoc2lkLFxuICAgICAgICB0aGlzLmdldEFjdGlvbnMoc2VydmljZU5hbWUsIHN1cHBvcnRlZENhcGFiaWxpdHkpLFxuICAgICAgICBhY2Nlc3NTcGVjLmFsbG93UHJpbmNpcGFsQXJucyxcbiAgICAgICAgYXJuQ29uZGl0aW9uVGVzdCxcbiAgICAgICAgcmVzb3VyY2VBcm5zKTtcbiAgICAgIHBvbGljeVN0YXRlbWVudHMucHVzaChzdGF0ZW1lbnQpO1xuICAgIH1cbiAgICByZXR1cm4gcG9saWN5U3RhdGVtZW50cztcbiAgfVxuXG4gIG1ha2VBbGxvd1N0YXRlbWVudChzaWQ6IHN0cmluZyxcbiAgICBhY3Rpb25zOiBBcnJheTxzdHJpbmc+LFxuICAgIHByaW5jaXBhbEFybnM6IEFycmF5PHN0cmluZz4sXG4gICAgdGVzdDogQXJuQ29uZGl0aW9uVGVzdCxcbiAgICByZXNvdXJjZXM6IEFycmF5PHN0cmluZz4pOiBQb2xpY3lTdGF0ZW1lbnQge1xuICAgIGNvbnN0IHBvbGljeVN0YXRlbWVudFByb3BzOiBQb2xpY3lTdGF0ZW1lbnRQcm9wcyA9IHtcbiAgICAgIHNpZDogc2lkLFxuICAgICAgZWZmZWN0OiBFZmZlY3QuQUxMT1csXG4gICAgfTtcbiAgICBjb25zdCBzdGF0ZW1lbnQgPSBuZXcgUG9saWN5U3RhdGVtZW50KHBvbGljeVN0YXRlbWVudFByb3BzKTtcbiAgICBzdGF0ZW1lbnQuYWRkQWN0aW9ucyguLi5hY3Rpb25zKTtcbiAgICBzdGF0ZW1lbnQuYWRkQW55UHJpbmNpcGFsKCk7XG4gICAgc3RhdGVtZW50LmFkZFJlc291cmNlcyguLi5yZXNvdXJjZXMpO1xuICAgIHN0YXRlbWVudC5hZGRDb25kaXRpb24odGVzdCwgeyAnYXdzOlByaW5jaXBhbEFybic6IEs5UG9saWN5RmFjdG9yeS5kZWR1cGxpY2F0ZVByaW5jaXBhbHMocHJpbmNpcGFsQXJucykgfSk7XG4gICAgcmV0dXJuIHN0YXRlbWVudDtcbiAgfVxuXG4gIHdhc0xpa2VVc2VkKGFjY2Vzc1NwZWNzOiBJQWNjZXNzU3BlY1tdKTogYm9vbGVhbiB7XG4gICAgZm9yIChsZXQgYWNjZXNzU3BlYyBvZiBhY2Nlc3NTcGVjcykge1xuICAgICAgaWYgKCdBcm5MaWtlJyA9PSBhY2Nlc3NTcGVjLnRlc3QpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGdldEFsbG93ZWRQcmluY2lwYWxBcm5zKGFjY2Vzc1NwZWNzOiBJQWNjZXNzU3BlY1tdKTogQXJyYXk8c3RyaW5nPiB7XG4gICAgbGV0IGFsbG93ZWRQcmluY2lwYWxBcm5zID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gICAgZm9yIChsZXQgYWNjZXNzU3BlYyBvZiBhY2Nlc3NTcGVjcykge1xuICAgICAgYWNjZXNzU3BlYy5hbGxvd1ByaW5jaXBhbEFybnMuZm9yRWFjaChmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgYWxsb3dlZFByaW5jaXBhbEFybnMuYWRkKHZhbHVlKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gQXJyYXkuZnJvbShhbGxvd2VkUHJpbmNpcGFsQXJucyk7XG4gIH1cblxuICAvKipcbiAgICAgKiBrOSB3YW50cyB0byBkZW55IGFsbCBBV1MgYWNjb3VudHMgYW5kIElBTSBwcmluY2lwYWxzIG5vdCBleHBsaWNpdGx5IGFsbG93ZWQ7IHRoaXMgKnNob3VsZCpcbiAgICAgKiBiZSBzdHJhaWdodGZvcndhcmQsIGJ1dCBpdCBpc24ndCBiZWNhdXNlIG9mIHRoZSB3YXkgYXdzLWNkayBtZXJnZXMgYW5kIG1hbmlwdWxhdGVzIFByaW5jaXBhbHMuXG4gICAgICogQHJldHVybiBsaXN0IG9mIHByaW5jaXBhbHMgZm9yIGEgRGVueUV2ZXJ5b25lRWxzZSBzdGF0ZW1lbnRcbiAgICAgKi9cbiAgbWFrZURlbnlFdmVyeW9uZUVsc2VQcmluY2lwYWxzKCk6IEFyblByaW5jaXBhbFtdIHtcbiAgICAvKipcbiAgICAgICAgICogV2Ugc2hvdWxkIGJlIGFibGUgdG8gcHJvdmlkZSBBbnlQcmluY2lwYWwgb25jZSAob2YgY291cnNlKSwgYnV0IEFXUyBDREsgY29udmVydHM6XG4gICAgICAgICAqIFwiUHJpbmNpcGFsXCI6IHtcbiAgICAgICAgICogICBcIkFXU1wiOiBcIipcIiAgICAvLyBpZGVudGlmaWVzIGFsbCBBV1MgYWNjb3VudHMgYW5kIElBTS5cbiAgICAgICAgICogfVxuICAgICAgICAgKiB0bzpcbiAgICAgICAgICogXCJQcmluY2lwYWxcIjogXCIqXCIgIC8vIGlkZW50aWZpZXMgYWxsIHByaW5jaXBhbHMgaW5jbHVkaW5nIEFXUyBTZXJ2aWNlIHByaW5jaXBhbHNcbiAgICAgICAgICpcbiAgICAgICAgICogVGhhdCdzIGEgZ3JlYXRlciBzY29wZSB0aGFuIHdlIHdhbnQuXG4gICAgICAgICAqXG4gICAgICAgICAqIFNvIHByb3ZpZGUgQW55UHJpbmNpcGFsIHR3aWNlLCBzbyBhd3MtY2RrIG1haW50YWlucyB0aGUgYXJyYXkgZm9ybS5cbiAgICAgICAgICpcbiAgICAgICAgICogQVdTIHJld3JpdGVzIHRoZSBBV1MgbWVtYmVyIG9mIHRoZSBwb2xpY3kgb24gc2F2ZSBzb1xuICAgICAgICAgKiBvbmx5IHRoZSB1bmlxdWUgc2V0IG9mIHByaW5jaXBhbHMgYXJlIGluY2x1ZGVkXG4gICAgICAgICAqIFNvIGFmdGVyIHRoZXNlIG1hY2hpbmF0aW9ucywgd2UgZW5kIHVwIHdpdGggd2hhdCB3ZSB3YW50LlxuICAgICAgICAgKi9cbiAgICByZXR1cm4gW25ldyBBbnlQcmluY2lwYWwoKSwgbmV3IEFueVByaW5jaXBhbCgpXTtcbiAgfVxuXG59XG4iXX0=