UNPKG

@k9securityio/k9-cdk

Version:

Provision strong AWS security policies easily using the AWS CDK.

355 lines 53.4 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.K9PolicyFactory = exports.SID_DENY_UNTRUSTED_ORGS = exports.AccessCapability = void 0; exports.getAccessCapabilityFromValue = getAccessCapabilityFromValue; exports.canPrincipalsManageResources = canPrincipalsManageResources; exports.hasWildcardPrincipal = hasWildcardPrincipal; exports.validateAccessSpecs = validateAccessSpecs; exports.toPascalCase = toPascalCase; 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}`); } /** * 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; } /** * Check if any access spec contains a wildcard principal ("*"). */ function hasWildcardPrincipal(accessSpecs) { for (let spec of accessSpecs) { if (spec.allowPrincipalArns.includes('*')) { return true; } } return false; } /** * Validate that access specs have valid principal ARN + org constraint combinations. * Throws an error for invalid combinations: * - Empty allowPrincipalArns * - Wildcard allowPrincipalArns without restrictToPrincipalOrgIDs (public access) */ function validateAccessSpecs(accessSpecs) { for (let spec of accessSpecs) { if (!spec.allowPrincipalArns || spec.allowPrincipalArns.length === 0) { throw new Error('allowPrincipalArns must not be empty; every resource policy statement requires a Principal element.'); } if (spec.allowPrincipalArns.includes('*') && (!spec.restrictToPrincipalOrgIDs || spec.restrictToPrincipalOrgIDs.length === 0)) { throw new Error('k9-cdk will not generate a resource policy that allows fully public access.' + ' Wildcard principal ("*") requires restrictToPrincipalOrgIDs to scope access.' + ' Consider specifying account principal ARNs or constraining to specific PrincipalOrgIDs.'); } } } /** * 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.SID_DENY_UNTRUSTED_ORGS = 'DenyUntrustedOrgs'; class K9PolicyFactory { constructor() { /** @internal */ this._SUPPORTED_SERVICES = new Set([ 'S3', 'KMS', 'DynamoDB', 'SQS', 'EventBridge', ]); /** @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; } } // Merge restrictToPrincipalOrgIDs if (addition.restrictToPrincipalOrgIDs && addition.restrictToPrincipalOrgIDs.length > 0) { if (!target.restrictToPrincipalOrgIDs) { target.restrictToPrincipalOrgIDs = []; } target.restrictToPrincipalOrgIDs.push(...addition.restrictToPrincipalOrgIDs); } } 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, accessSpec.restrictToPrincipalOrgIDs); policyStatements.push(statement); } return policyStatements; } makeAllowStatement(sid, actions, principalArns, test, resources, restrictToPrincipalOrgIDs) { 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); const isWildcardPrincipal = principalArns.includes('*'); const hasOrgConstraint = restrictToPrincipalOrgIDs && restrictToPrincipalOrgIDs.length > 0; if (isWildcardPrincipal && hasOrgConstraint) { // Code Path B: wildcard + org constraint // Use Principal: "*" (already added via addAnyPrincipal) + aws:PrincipalOrgID condition // Do NOT add aws:PrincipalArn condition statement.addCondition('StringEquals', { 'aws:PrincipalOrgID': restrictToPrincipalOrgIDs }); } else { // Code Path A: specific principal ARNs (existing behavior) statement.addCondition(test, { 'aws:PrincipalArn': K9PolicyFactory.deduplicatePrincipals(principalArns) }); if (hasOrgConstraint) { // Specific ARNs + org constraint: both conditions must be true statement.addCondition('StringEquals', { 'aws:PrincipalOrgID': restrictToPrincipalOrgIDs }); } } 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()]; } /** * Create a DenyUntrustedOrgs statement that explicitly denies principals from * untrusted orgs for org-restricted actions. This provides defense-in-depth * beyond the implicit deny from org-constrained Allow statements. * * The StringNotEquals condition on aws:PrincipalOrgID is inherently safe for * AWS service principals because the key is absent from their request context, * so the condition is not satisfied and the Deny does not apply. * * @return a PolicyStatement with Effect Deny, or undefined if no access specs have org restrictions * @internal */ _makeDenyUntrustedOrgsStatement(serviceName, supportedCapabilities, accessSpecsByCapability, resourceArns) { const allActions = new Set(); const allOrgIDs = new Set(); for (let capability of supportedCapabilities) { const accessSpec = accessSpecsByCapability.get(capability); if (accessSpec?.restrictToPrincipalOrgIDs && accessSpec.restrictToPrincipalOrgIDs.length > 0) { const actions = this.getActions(serviceName, capability); for (let action of actions) { allActions.add(action); } for (let orgID of accessSpec.restrictToPrincipalOrgIDs) { allOrgIDs.add(orgID); } } } if (allActions.size === 0) { return undefined; } const statement = new aws_iam_1.PolicyStatement({ sid: exports.SID_DENY_UNTRUSTED_ORGS, effect: aws_iam_1.Effect.DENY, principals: this.makeDenyEveryoneElsePrincipals(), actions: Array.from(allActions), resources: resourceArns, }); statement.addCondition('Bool', { 'aws:PrincipalIsAWSService': ['false'], }); statement.addCondition('StringNotEquals', { 'aws:PrincipalOrgID': Array.from(allOrgIDs), }); return statement; } } exports.K9PolicyFactory = K9PolicyFactory; _a = JSII_RTTI_SYMBOL_1; K9PolicyFactory[_a] = { fqn: "@k9securityio/k9-cdk.k9policy.K9PolicyFactory", version: "2.2.1" }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiazlwb2xpY3kuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvazlwb2xpY3kudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQXlCQSxvRUFZQztBQTJDRCxvRUFhQztBQU1ELG9EQU9DO0FBUUQsa0RBZ0JDO0FBUUQsb0NBYUM7O0FBdkpELGlEQU82QjtBQVU3QixJQUFZLGdCQU1YO0FBTkQsV0FBWSxnQkFBZ0I7SUFDMUIsK0RBQTJDLENBQUE7SUFDM0MsK0NBQTJCLENBQUE7SUFDM0IsMkNBQXVCLENBQUE7SUFDdkIsNkNBQXlCLENBQUE7SUFDekIsK0NBQTJCLENBQUE7QUFDN0IsQ0FBQyxFQU5XLGdCQUFnQixnQ0FBaEIsZ0JBQWdCLFFBTTNCO0FBRUQsU0FBZ0IsNEJBQTRCLENBQUMsbUJBQTJCO0lBQ3RFLDJEQUEyRDtJQUMzRCxLQUFLLElBQUksR0FBRyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxDQUFDO1FBQzlDLGFBQWE7UUFDYixJQUFJLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxJQUFJLG1CQUFtQixFQUFFLENBQUM7WUFDakQsK0ZBQStGO1lBQy9GLElBQUksUUFBUSxHQUFrQyxHQUFHLENBQUM7WUFDbEQsT0FBTyxnQkFBZ0IsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNwQyxDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU0sS0FBSyxDQUFDLDhDQUE4QyxtQkFBbUIsRUFBRSxDQUFDLENBQUM7QUFDbkYsQ0FBQztBQXFDRDs7Ozs7R0FLRztBQUNILFNBQWdCLDRCQUE0QixDQUFDLHVCQUEyRDtJQUN0RyxJQUFJLFNBQVMsR0FBRyx1QkFBdUIsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsbUJBQW1CLENBQUMsQ0FBQztJQUNsRixJQUFJLGNBQWMsR0FBRyx1QkFBdUIsQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsV0FBVyxDQUFDLENBQUM7SUFFL0UsSUFBSSxDQUFDLFNBQVMsRUFB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QUFDO1FBQ0gsQ0FBQztRQUVELElBQUksVUFBVSxDQUFDLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUMxQixPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBRUQsTUFBTSxTQUFTLEdBQUcsSUFBSSx5QkFBZSxDQUFDO1lBQ3BDLEdBQUcsRUFBRSwrQkFBdUI7WUFDNUIsTUFBTSxFQUFFLGdCQUFNLENBQUMsSUFBSTtZQUNuQixVQUFVLEVBQUUsSUFBSSxDQUFDLDhCQUE4QixFQUFFO1lBQ2pELE9BQU8sRUFBRSxLQUFLLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQztZQUMvQixTQUFTLEVBQUUsWUFBWTtTQUN4QixDQUFDLENBQUM7UUFDSCxTQUFTLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRTtZQUM3QiwyQkFBMkIsRUFBRSxDQUFDLE9BQU8sQ0FBQztTQUN2QyxDQUFDLENBQUM7UUFDSCxTQUFTLENBQUMsWUFBWSxDQUFDLGlCQUFpQixFQUFFO1lBQ3hDLG9CQUFvQixFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO1NBQzVDLENBQUMsQ0FBQztRQUVILE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7O0FBNVNILDBDQThTQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEFueVByaW5jaXBhbCxcbiAgQXJuUHJpbmNpcGFsLFxuICBDb25kaXRpb25zLFxuICBFZmZlY3QsXG4gIFBvbGljeVN0YXRlbWVudCxcbiAgUG9saWN5U3RhdGVtZW50UHJvcHMsXG59IGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuXG5leHBvcnQgdHlwZSBBcm5FcXVhbHNUZXN0ID0gJ0FybkVxdWFscydcblxuZXhwb3J0IHR5cGUgQXJuTGlrZVRlc3QgPSAnQXJuTGlrZSc7XG5cbmV4cG9ydCB0eXBlIEFybkNvbmRpdGlvblRlc3QgPVxuICAgIHwgQXJuRXF1YWxzVGVzdFxuICAgIHwgQXJuTGlrZVRlc3Q7XG5cbmV4cG9ydCBlbnVtIEFjY2Vzc0NhcGFiaWxpdHkge1xuICBBRE1JTklTVEVSX1JFU09VUkNFID0gJ2FkbWluaXN0ZXItcmVzb3VyY2UnLFxuICBSRUFEX0NPTkZJRyA9ICdyZWFkLWNvbmZpZycsXG4gIFJFQURfREFUQSA9ICdyZWFkLWRhdGEnLFxuICBXUklURV9EQVRBID0gJ3dyaXRlLWRhdGEnLFxuICBERUxFVEVfREFUQSA9ICdkZWxldGUtZGF0YScsXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRBY2Nlc3NDYXBhYmlsaXR5RnJvbVZhbHVlKGFjY2Vzc0NhcGFiaWxpdHlTdHI6IHN0cmluZyk6IEFjY2Vzc0NhcGFiaWxpdHkge1xuICAvL2h0dHBzOi8vYmxvZy5sb2dyb2NrZXQuY29tL3R5cGVzY3JpcHQtc3RyaW5nLWVudW1zLWd1aWRlL1xuICBmb3IgKGxldCBrZXkgb2YgT2JqZWN0LmtleXMoQWNjZXNzQ2FwYWJpbGl0eSkpIHtcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgaWYgKEFjY2Vzc0NhcGFiaWxpdHlba2V5XSA9PSBhY2Nlc3NDYXBhYmlsaXR5U3RyKSB7XG4gICAgICAvLyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNzM4MDg0NS9ob3ctZG8taS1jb252ZXJ0LWEtc3RyaW5nLXRvLWVudW0taW4tdHlwZXNjcmlwdFxuICAgICAgbGV0IHR5cGVkS2V5ID0gPGtleW9mIHR5cGVvZiBBY2Nlc3NDYXBhYmlsaXR5PmtleTtcbiAgICAgIHJldHVybiBBY2Nlc3NDYXBhYmlsaXR5W3R5cGVkS2V5XTtcbiAgICB9XG4gIH1cblxuICB0aHJvdyBFcnJvcihgQ291bGQgbm90IGdldCBBY2Nlc3NDYXBhYmlsaXR5IGZyb20gdmFsdWU6ICR7YWNjZXNzQ2FwYWJpbGl0eVN0cn1gKTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJQWNjZXNzU3BlYyB7XG4gIGFjY2Vzc0NhcGFiaWxpdGllczogQXJyYXk8QWNjZXNzQ2FwYWJpbGl0eT4gfCBBY2Nlc3NDYXBhYmlsaXR5O1xuICBhbGxvd1ByaW5jaXBhbEFybnM6IEFycmF5PHN0cmluZz47XG4gIHRlc3Q/OiBBcm5Db25kaXRpb25UZXN0O1xuICAvKipcbiAgICogT3B0aW9uYWwgbGlzdCBvZiBBV1MgT3JnYW5pemF0aW9uIElEcyB0aGF0IHJlc3RyaWN0IHRoZSBwcmluY2lwYWxzIHNwZWNpZmllZFxuICAgKiBpbiBgYWxsb3dQcmluY2lwYWxBcm5zYC4gV2hlbiBwcmVzZW50LCBnZW5lcmF0ZWQgQWxsb3cgc3RhdGVtZW50cyB3aWxsIGluY2x1ZGVcbiAgICogYSBgU3RyaW5nRXF1YWxzYCBjb25kaXRpb24gb24gYGF3czpQcmluY2lwYWxPcmdJRGAgYW5kIGEgRGVueVVudHJ1c3RlZE9yZ3Mgc3RhdGVtZW50IHdpbGxcbiAgICogYmUgZ2VuZXJhdGVkIGZvciB0aGUgcGVybWlzc2lvbnMgdGhhdCBhcmUgcmVzdHJpY3RlZCBieSBvcmcgSURzLlxuICAgKlxuICAgKiBPcmcgSURzIHJlc3RyaWN0IHdoaWNoIHByaW5jaXBhbHMgYXJlIGFsbG93ZWQg4oCUIHRoZXkgZG8gbm90IHJlcGxhY2VcbiAgICogYGFsbG93UHJpbmNpcGFsQXJuc2AuIElmIHlvdSB3YW50IHRvIGFsbG93IGFuIGVudGlyZSBvcmcsIGFkZCBgKmAgdG8gYGFsbG93UHJpbmNpcGFsQXJuc2AgYW5kIHRoZSBvcmcgSUQgdG9cbiAgICogYHJlc3RyaWN0VG9QcmluY2lwYWxPcmdJRHNgLlxuICAgKi9cbiAgcmVzdHJpY3RUb1ByaW5jaXBhbE9yZ0lEcz86IEFycmF5PHN0cmluZz47XG59XG5cbi8qKlxuICogYElBV1NTZXJ2aWNlQWNjZXNzR2VuZXJhdG9yYCBkZWZpbmVzIGFuIGludGVyZmFjZSB0aGF0IHRoZSBrOSBwb2xpY3kgZ2VuZXJhdG9ycyB1c2UgdG8gZ3JhbnQgYW4gQVdTIHNlcnZpY2VcbiAqIGFjY2VzcyB0byBhIHByb3RlY3RlZCByZXNvdXJjZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJQVdTU2VydmljZUFjY2Vzc0dlbmVyYXRvciB7XG4gIC8qKlxuICAgKiBNYWtlIGFuIGFycmF5IG9mIFBvbGljeVN0YXRlbWVudCBvYmplY3RzIHRoYXQgYWxsb3cgYW4gQVdTIHNlcnZpY2UsIGUuZy4gQ2xvdWRGcm9udCwgdG8gYWNjZXNzIHRvIHRoZVxuICAgKiBwcm90ZWN0ZWQgQVdTIHJlc291cmNlLlxuICAgKi9cbiAgbWFrZUFsbG93U3RhdGVtZW50cygpOiBBcnJheTxQb2xpY3lTdGF0ZW1lbnQ+O1xuXG4gIC8qKlxuICAgKiBNYWtlIGEgQ29uZGl0aW9ucyBvYmplY3QgdGhhdCBjcmVhdGVzIGFuIGV4Y2VwdGlvbiBmb3IgYW4gQVdTIHNlcnZpY2UgaW4gYSBwcm90ZWN0ZWQgcmVzb3VyY2UncyBgRGVueUV2ZXJ5b25lRWxzZWBcbiAgICogc3RhdGVtZW50LlxuICAgKi9cbiAgbWFrZUNvbmRpdGlvbnNUb0V4Y2VwdEZyb21EZW55RXZlcnlvbmVFbHNlKCk6IENvbmRpdGlvbnM7XG59XG5cbi8qKlxuICogQ2hlY2sgd2hldGhlciB0aGUgcHJvdmlkZWQgYWNjZXNzIHNwZWNzIGVuc3VyZSB0aGF0IGF0IGxlYXN0IG9uZSBwcmluY2lwYWwgY2FuIGJvdGggcmVhZCBhbmQgYWRtaW5pc3RlciBjb25maWd1cmF0aW9uLlxuICogQHBhcmFtIGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5IGlzIGEgbWFwIG9mIGFjY2VzcyBzcGVjcyBrZXllZCBieSBhY2Nlc3MgY2FwYWJpbGl0eVxuICpcbiAqIEByZXR1cm4gdHJ1ZSB3aGVuIGF0IGxlYXN0IG9uZSBwcmluY2lwYWwgdGhhdCBjYW4gYWRtaW5pc3RlciBhbmQgcmVhZCBjb25maWd1cmF0aW9uIGV4aXN0c1xuICovXG5leHBvcnQgZnVuY3Rpb24gY2FuUHJpbmNpcGFsc01hbmFnZVJlc291cmNlcyhhY2Nlc3NTcGVjc0J5Q2FwYWJpbGl0eTogTWFwPEFjY2Vzc0NhcGFiaWxpdHksIElBY2Nlc3NTcGVjPikge1xuICBsZXQgYWRtaW5TcGVjID0gYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHkuZ2V0KEFjY2Vzc0NhcGFiaWxpdHkuQURNSU5JU1RFUl9SRVNPVVJDRSk7XG4gIGxldCByZWFkQ29uZmlnU3BlYyA9IGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5LmdldChBY2Nlc3NDYXBhYmlsaXR5LlJFQURfQ09ORklHKTtcblxuICBpZiAoKGFkbWluU3BlYz8uYWxsb3dQcmluY2lwYWxBcm5zICYmIGFkbWluU3BlYy5hbGxvd1ByaW5jaXBhbEFybnMubGVuZ3RoID4gMClcbiAgICAgICAgJiYgKHJlYWRDb25maWdTcGVjPy5hbGxvd1ByaW5jaXBhbEFybnMgJiYgcmVhZENvbmZpZ1NwZWMuYWxsb3dQcmluY2lwYWxBcm5zLmxlbmd0aCA+IDApKSB7XG4gICAgY29uc3QgYWRtaW5QcmluY2lwYWxzID0gbmV3IFNldDxzdHJpbmc+KGFkbWluU3BlYy5hbGxvd1ByaW5jaXBhbEFybnMpO1xuICAgIGNvbnN0IHJlYWRDb25maWdQcmluY2lwYWxzID0gbmV3IFNldDxzdHJpbmc+KHJlYWRDb25maWdTcGVjLmFsbG93UHJpbmNpcGFsQXJucyk7XG4gICAgY29uc3QgaW50ZXJzZWN0aW9uID0gbmV3IFNldChcbiAgICAgIFsuLi5hZG1pblByaW5jaXBhbHNdLmZpbHRlcih4ID0+IHJlYWRDb25maWdQcmluY2lwYWxzLmhhcyh4KSkpO1xuICAgIHJldHVybiBpbnRlcnNlY3Rpb24uc2l6ZSA+IDA7XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG5cbi8qKlxuICogQ2hlY2sgaWYgYW55IGFjY2VzcyBzcGVjIGNvbnRhaW5zIGEgd2lsZGNhcmQgcHJpbmNpcGFsIChcIipcIikuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBoYXNXaWxkY2FyZFByaW5jaXBhbChhY2Nlc3NTcGVjczogQXJyYXk8SUFjY2Vzc1NwZWM+KTogYm9vbGVhbiB7XG4gIGZvciAobGV0IHNwZWMgb2YgYWNjZXNzU3BlY3MpIHtcbiAgICBpZiAoc3BlYy5hbGxvd1ByaW5jaXBhbEFybnMuaW5jbHVkZXMoJyonKSkge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZSB0aGF0IGFjY2VzcyBzcGVjcyBoYXZlIHZhbGlkIHByaW5jaXBhbCBBUk4gKyBvcmcgY29uc3RyYWludCBjb21iaW5hdGlvbnMuXG4gKiBUaHJvd3MgYW4gZXJyb3IgZm9yIGludmFsaWQgY29tYmluYXRpb25zOlxuICogLSBFbXB0eSBhbGxvd1ByaW5jaXBhbEFybnNcbiAqIC0gV2lsZGNhcmQgYWxsb3dQcmluY2lwYWxBcm5zIHdpdGhvdXQgcmVzdHJpY3RUb1ByaW5jaXBhbE9yZ0lEcyAocHVibGljIGFjY2VzcylcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlQWNjZXNzU3BlY3MoYWNjZXNzU3BlY3M6IEFycmF5PElBY2Nlc3NTcGVjPik6IHZvaWQge1xuICBmb3IgKGxldCBzcGVjIG9mIGFjY2Vzc1NwZWNzKSB7XG4gICAgaWYgKCFzcGVjLmFsbG93UHJpbmNpcGFsQXJucyB8fCBzcGVjLmFsbG93UHJpbmNpcGFsQXJucy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ2FsbG93UHJpbmNpcGFsQXJucyBtdXN0IG5vdCBiZSBlbXB0eTsgZXZlcnkgcmVzb3VyY2UgcG9saWN5IHN0YXRlbWVudCByZXF1aXJlcyBhIFByaW5jaXBhbCBlbGVtZW50LicsXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAoc3BlYy5hbGxvd1ByaW5jaXBhbEFybnMuaW5jbHVkZXMoJyonKSAmJlxuICAgICAgICAoIXNwZWMucmVzdHJpY3RUb1ByaW5jaXBhbE9yZ0lEcyB8fCBzcGVjLnJlc3RyaWN0VG9QcmluY2lwYWxPcmdJRHMubGVuZ3RoID09PSAwKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnazktY2RrIHdpbGwgbm90IGdlbmVyYXRlIGEgcmVzb3VyY2UgcG9saWN5IHRoYXQgYWxsb3dzIGZ1bGx5IHB1YmxpYyBhY2Nlc3MuJyArXG4gICAgICAgICcgV2lsZGNhcmQgcHJpbmNpcGFsIChcIipcIikgcmVxdWlyZXMgcmVzdHJpY3RUb1ByaW5jaXBhbE9yZ0lEcyB0byBzY29wZSBhY2Nlc3MuJyArXG4gICAgICAgICcgQ29uc2lkZXIgc3BlY2lmeWluZyBhY2NvdW50IHByaW5jaXBhbCBBUk5zIG9yIGNvbnN0cmFpbmluZyB0byBzcGVjaWZpYyBQcmluY2lwYWxPcmdJRHMuJyxcbiAgICAgICk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ29udmVydHMgYSBzdHJpbmcgdG8gUGFzY2FsQ2FzZSwgd2hpY2ggaXMgdXNlZnVsIGZvciBlLmcuIHBvbGljeSB0eXBlcyB0aGF0IGRvbid0XG4gKiBkbyBub3Qgc3VwcG9ydCBzcGFjZXMgb3IgaHlwaGVucyBpbiBzdGF0ZW1lbnQgaWRzLlxuICpcbiAqIEBwYXJhbSBpbnB1dFxuICovXG5leHBvcnQgZnVuY3Rpb24gdG9QYXNjYWxDYXNlKGlucHV0OiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBSZW1vdmUgcGxhY2Vob2xkZXJzIGxpa2UgJHtzb21ldGhpbmd9IGFuZCB0cmltIHdoaXRlc3BhY2VcbiAgY29uc3QgY2xlYW5lZElucHV0ID0gaW5wdXQucmVwbGFjZSgvXFwkXFx7Lio/XFx9L2csICcnKS50cmltKCk7XG5cbiAgLy8gU3BsaXQgdGhlIGlucHV0IGludG8gd29yZHMgYmFzZWQgb24gc3BhY2VzLCBoeXBoZW5zLCB1bmRlcnNjb3Jlcywgb3Igb3RoZXIgZGVsaW1pdGVyc1xuICBjb25zdCB3b3JkcyA9IGNsZWFuZWRJbnB1dC5zcGxpdCgvW1xcc19cXC1dKy8pO1xuXG4gIC8vIENvbnZlcnQgZWFjaCB3b3JkIHRvIFBhc2NhbENhc2VcbiAgcmV0dXJuIHdvcmRzXG4gICAgLm1hcChcbiAgICAgIHdvcmQgPT4gd29yZC5jaGFyQXQoMCkudG9VcHBlckNhc2UoKSArIHdvcmQuc2xpY2UoMSkudG9Mb3dlckNhc2UoKSwgLy8gQ2FwaXRhbGl6ZSB0aGUgZmlyc3QgbGV0dGVyLCBsb3dlciB0aGUgcmVzdFxuICAgIClcbiAgICAuam9pbignJyk7XG59XG5cbmV4cG9ydCBjb25zdCBTSURfREVOWV9VTlRSVVNURURfT1JHUyA9ICdEZW55VW50cnVzdGVkT3Jncyc7XG5cbmV4cG9ydCBjbGFzcyBLOVBvbGljeUZhY3Rvcnkge1xuXG4gIC8qKlxuICAgKiBEZWR1cGxpY2F0ZSBhbiBhcnJheSBvZiBwcmluY2lwYWxzIHdoaWxlIHByZXNlcnZpbmcgb3JpZ2luYWwgb3JkZXIgb2YgcHJpbmNpcGFscy5cbiAgICogTm90ZSB0aGF0IHByaW5jaXBhbHMgbWF5IGNvbnRhaW4gZWl0aGVyIHN0cmluZ3Mgb3Igb2JqZWN0cywgc28gbmFpdmUgYXJyYXkgc29ydGluZ1xuICAgKiBwcm9kdWNlcyB1bnN0YWJsZSByZXN1bHRzLlxuICAgKlxuICAgKiBAcGFyYW0gcHJpbmNpcGFsc1xuICAgKi9cbiAgc3RhdGljIGRlZHVwbGljYXRlUHJpbmNpcGFscyhwcmluY2lwYWxzOiBBcnJheTxzdHJpbmd8b2JqZWN0Pik6IEFycmF5PHN0cmluZ3xvYmplY3Q+IHtcbiAgICBjb25zdCBvYnNlcnZlZFByaW5jaXBhbHMgPSBuZXcgU2V0PHN0cmluZ3xvYmplY3Q+KCk7XG4gICAgY29uc3QgdW5pcXVlUHJpbmNpcGFscyA9IG5ldyBBcnJheTxzdHJpbmd8b2JqZWN0PigpO1xuICAgIGZvciAobGV0IHByaW5jaXBhbCBvZiBwcmluY2lwYWxzKSB7XG4gICAgICBpZiAoIW9ic2VydmVkUHJpbmNpcGFscy5oYXMocHJpbmNpcGFsKSkge1xuICAgICAgICB1bmlxdWVQcmluY2lwYWxzLnB1c2gocHJpbmNpcGFsKTtcbiAgICAgICAgb2JzZXJ2ZWRQcmluY2lwYWxzLmFkZChwcmluY2lwYWwpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdW5pcXVlUHJpbmNpcGFscztcbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgX1NVUFBPUlRFRF9TRVJWSUNFUyA9IG5ldyBTZXQ8c3RyaW5nPihbXG4gICAgJ1MzJyxcbiAgICAnS01TJyxcbiAgICAnRHluYW1vREInLFxuICAgICdTUVMnLFxuICAgICdFdmVudEJyaWRnZScsXG4gIF0pO1xuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgX0s5Q2FwYWJpbGl0eU1hcEpTT046IE9iamVjdCA9IHJlcXVpcmUoJy4uL3Jlc291cmNlcy9jYXBhYmlsaXR5X3N1bW1hcnkuanNvbicpOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgLyoqIEBpbnRlcm5hbCAqL1xuICBfSzlDYXBhYmlsaXR5TWFwQnlTZXJ2aWNlOiBNYXA8c3RyaW5nLCBPYmplY3Q+ID0gbmV3IE1hcChPYmplY3QuZW50cmllcyh0aGlzLl9LOUNhcGFiaWxpdHlNYXBKU09OKSk7XG5cbiAgZ2V0QWN0aW9ucyhzZXJ2aWNlOiBzdHJpbmcsIGFjY2Vzc0NhcGFiaWxpdHk6IEFjY2Vzc0NhcGFiaWxpdHkpOiBBcnJheTxzdHJpbmc+IHtcbiAgICBpZiAoIXRoaXMuX1NVUFBPUlRFRF9TRVJWSUNFUy5oYXMoc2VydmljZSkgJiYgdGhpcy5fSzlDYXBhYmlsaXR5TWFwQnlTZXJ2aWNlLmhhcyhzZXJ2aWNlKSkge1xuICAgICAgdGhyb3cgRXJyb3IoYHVuc3VwcG9ydGVkIHNlcnZpY2U6ICR7c2VydmljZX1gKTtcbiAgICB9XG5cbiAgICBsZXQgc2VydmljZUNhcGFiaWxpdGllc09iajogT2JqZWN0ID0gdGhpcy5fSzlDYXBhYmlsaXR5TWFwQnlTZXJ2aWNlLmdldChzZXJ2aWNlKSB8fCB7fTtcbiAgICBsZXQgc2VydmljZUNhcGFiaWxpdGllc01hcCA9IG5ldyBNYXA8c3RyaW5nLCBBcnJheTxzdHJpbmc+PihPYmplY3QuZW50cmllcyhzZXJ2aWNlQ2FwYWJpbGl0aWVzT2JqKSk7XG5cbiAgICBsZXQgYWNjZXNzQ2FwYWJpbGl0eU5hbWUgPSBhY2Nlc3NDYXBhYmlsaXR5LnRvU3RyaW5nKCk7XG4gICAgaWYgKHNlcnZpY2VDYXBhYmlsaXRpZXNNYXAgJiZcbiAgICAgICAgICAgIHNlcnZpY2VDYXBhYmlsaXRpZXNNYXAuaGFzKGFjY2Vzc0NhcGFiaWxpdHlOYW1lKSkge1xuICAgICAgcmV0dXJuIHNlcnZpY2VDYXBhYmlsaXRpZXNNYXAuZ2V0KGFjY2Vzc0NhcGFiaWxpdHlOYW1lKSB8fCBBcnJheTxzdHJpbmc+KCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBuZXcgQXJyYXk8c3RyaW5nPigpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgX21lcmdlQWNjZXNzU3BlY3ModGFyZ2V0OiBJQWNjZXNzU3BlYywgYWRkaXRpb246IElBY2Nlc3NTcGVjKSB7XG4gICAgdGFyZ2V0LmFsbG93UHJpbmNpcGFsQXJucy5wdXNoKC4uLmFkZGl0aW9uLmFsbG93UHJpbmNpcGFsQXJucyk7XG4gICAgaWYgKHRhcmdldC50ZXN0KSB7XG4gICAgICAvL29rLCB1c2VyIGhhcyBzcGVjaWZpZWQgYSB0ZXN0IGF0IHNvbWUgcG9pbnQ7IGVuc3VyZSB0aGlzIGRlc2lyZWRBY2Nlc3NTcGVjLnRlc3QgbWF0Y2hlc1xuICAgICAgaWYgKHRhcmdldC50ZXN0ICE9IGFkZGl0aW9uLnRlc3QpIHtcbiAgICAgICAgbGV0IG1zZyA9ICdDYW5ub3QgbWVyZ2UgQWNjZXNzU3BlY3M7IHRlc3QgYXR0cmlidXRlcyBkbyBub3QgbWF0Y2g6JyArXG4gICAgICAgICAgICAgICAgICAgIGBcXG4ke0pTT04uc3RyaW5naWZ5KHRhcmdldCl9XFxuJHtKU09OLnN0cmluZ2lmeShhZGRpdGlvbil9YDtcbiAgICAgICAgdGhyb3cgRXJyb3IobXNnKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy9maXJzdCBleHBsaWNpdCB0ZXN0IHByZWZlcmVuY2Ugd2luc1xuICAgICAgaWYgKGFkZGl0aW9uLnRlc3QpIHtcbiAgICAgICAgdGFyZ2V0LnRlc3QgPSBhZGRpdGlvbi50ZXN0O1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIE1lcmdlIHJlc3RyaWN0VG9QcmluY2lwYWxPcmdJRHNcbiAgICBpZiAoYWRkaXRpb24ucmVzdHJpY3RUb1ByaW5jaXBhbE9yZ0lEcyAmJiBhZGRpdGlvbi5yZXN0cmljdFRvUHJpbmNpcGFsT3JnSURzLmxlbmd0aCA+IDApIHtcbiAgICAgIGlmICghdGFyZ2V0LnJlc3RyaWN0VG9QcmluY2lwYWxPcmdJRHMpIHtcbiAgICAgICAgdGFyZ2V0LnJlc3RyaWN0VG9QcmluY2lwYWxPcmdJRHMgPSBbXTtcbiAgICAgIH1cbiAgICAgIHRhcmdldC5yZXN0cmljdFRvUHJpbmNpcGFsT3JnSURzLnB1c2goLi4uYWRkaXRpb24ucmVzdHJpY3RUb1ByaW5jaXBhbE9yZ0lEcyk7XG4gICAgfVxuXG4gIH1cblxuICBtZXJnZURlc2lyZWRBY2Nlc3NTcGVjc0J5Q2FwYWJpbGl0eShzdXBwb3J0ZWRDYXBhYmlsaXRpZXM6IEFycmF5PEFjY2Vzc0NhcGFiaWxpdHk+LFxuICAgIGRlc2lyZWRBY2Nlc3M6IEFycmF5PElBY2Nlc3NTcGVjPik6IFJlY29yZDxzdHJpbmcsIElBY2Nlc3NTcGVjPiB7XG5cbiAgICBsZXQgYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHk6IE1hcDxBY2Nlc3NDYXBhYmlsaXR5LCBJQWNjZXNzU3BlYz4gPSBuZXcgTWFwPEFjY2Vzc0NhcGFiaWxpdHksIElBY2Nlc3NTcGVjPigpO1xuICAgIC8vIDEuIHBvcHVsYXRlIGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5IHdpdGggZnJlc2ggQWNjZXNzU3BlY3MgZm9yIGVhY2ggc3VwcG9ydGVkIGNhcGFiaWxpdHlcbiAgICAvLyAyLiBpdGVyYXRlIHRocm91Z2ggZGVzaXJlZEFjY2VzcyBzcGVjcyBhbmQgbWVyZ2UgZGF0YSBpbnRvIHdoYXQgd2UnbGwgdXNlXG4gICAgLy8gICAgaW1wb3J0YW50OiBkZXRlY3QgbWlzbWF0Y2hlZCB0ZXN0IHR5cGVzXG4gICAgLy8gICAgIHdlIGNhbiBsZWF2ZSBgdGVzdGAgdW5zZXQgaW4gdGhlIGRlZmF1bHQgYWNjZXNzIHNwZWNzXG4gICAgLy8gICAgIGFuZCBjb3B5IHRoZSB2YWx1ZSBmcm9tIHRoZSBzcGVjIGJlaW5nIG1lcmdlZCBpZiBpdCBpcyBzZXRcbiAgICAvLyAgICAgdGhyb3cgRXJyb3Igb24gbWlzbWF0Y2hcbiAgICAvLyAzLiBnZW5lcmF0ZSBhbiBBbGxvdyBzdGF0ZW1lbnQgZm9yIGVhY2ggc3VwcG9ydGVkIGNhcGFiaWxpdHlcblxuICAgIGZvciAobGV0IHN1cHBvcnRlZENhcGFiaWxpdHkgb2Ygc3VwcG9ydGVkQ2FwYWJpbGl0aWVzKSB7XG4gICAgICAvL2dlbmVyYXRlIGEgZGVmYXVsdCBhY2Nlc3Mgc3BlYyBmb3IgZWFjaCBvZiB0aGUgc2VydmljZSdzIHN1cHBvcnRlZCBjYXBhYmlsaXRpZXNcbiAgICAgIGxldCBlZmZlY3RpdmVBY2Nlc3NTcGVjOiBJQWNjZXNzU3BlYyA9IHtcbiAgICAgICAgYWNjZXNzQ2FwYWJpbGl0aWVzOiBzdXBwb3J0ZWRDYXBhYmlsaXR5LFxuICAgICAgICBhbGxvd1ByaW5jaXBhbEFybnM6IG5ldyBBcnJheTxzdHJpbmc+KCksXG4gICAgICAgIC8vIGxlYXZlICd0ZXN0JyBwcm9wZXJ0eSB1bnNldDsgd2lsbCBwb3B1bGF0ZSBmcm9tIHVzZXItcHJvdmlkZWQgZGF0YVxuICAgICAgfTtcbiAgICAgIGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5LnNldChzdXBwb3J0ZWRDYXBhYmlsaXR5LCBlZmZlY3RpdmVBY2Nlc3NTcGVjKTtcblxuICAgICAgLy9ub3cuLi4gbWVyZ2UgaW4gdGhlIHVzZXIncyBkZXNpcmVkIGFjY2VzcyBmb3IgdGhpcyBjYXBhYmlsaXR5XG4gICAgICBmb3IgKGxldCBkZXNpcmVkQWNjZXNzU3BlYyBvZiBkZXNpcmVkQWNjZXNzKSB7XG4gICAgICAgIGlmIChkZXNpcmVkQWNjZXNzU3BlYy5hY2Nlc3NDYXBhYmlsaXRpZXMgaW5zdGFuY2VvZiBBcnJheSkge1xuICAgICAgICAgIGZvciAobGV0IGRlc2lyZWRDYXBhYmlsaXR5IG9mIGRlc2lyZWRBY2Nlc3NTcGVjLmFjY2Vzc0NhcGFiaWxpdGllcykge1xuICAgICAgICAgICAgaWYgKHN1cHBvcnRlZENhcGFiaWxpdHkgPT0gZGVzaXJlZENhcGFiaWxpdHkpIHtcbiAgICAgICAgICAgICAgdGhpcy5fbWVyZ2VBY2Nlc3NTcGVjcyhlZmZlY3RpdmVBY2Nlc3NTcGVjLCBkZXNpcmVkQWNjZXNzU3BlYyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKHR5cGVvZiBkZXNpcmVkQWNjZXNzU3BlYy5hY2Nlc3NDYXBhYmlsaXRpZXMgPT0gJ3N0cmluZycpIHtcbiAgICAgICAgICBpZiAoc3VwcG9ydGVkQ2FwYWJpbGl0eSA9PSBkZXNpcmVkQWNjZXNzU3BlYy5hY2Nlc3NDYXBhYmlsaXRpZXMpIHtcbiAgICAgICAgICAgIHRoaXMuX21lcmdlQWNjZXNzU3BlY3MoZWZmZWN0aXZlQWNjZXNzU3BlYywgZGVzaXJlZEFjY2Vzc1NwZWMpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aHJvdyBFcnJvcihgVW5oYW5kbGVkIHR5cGUgb2YgYWNjZXNzQ2FwYWJpbGl0aWVzIGZvciAke2Rlc2lyZWRBY2Nlc3NTcGVjLmFjY2Vzc0NhcGFiaWxpdGllc31gKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHJlY29yZHM6IFJlY29yZDxzdHJpbmcsIElBY2Nlc3NTcGVjPiA9IHt9O1xuICAgIGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5LmZvckVhY2goZnVuY3Rpb24gKHZhbHVlLCBrZXkpIHtcbiAgICAgIHJlY29yZHNba2V5XSA9IHZhbHVlO1xuICAgIH0pO1xuICAgIHJldHVybiByZWNvcmRzO1xuICB9XG5cbiAgbWFrZUFsbG93U3RhdGVtZW50cyhzZXJ2aWNlTmFtZTogc3RyaW5nLFxuICAgIHN1cHBvcnRlZENhcGFiaWxpdGllczogQXJyYXk8QWNjZXNzQ2FwYWJpbGl0eT4sXG4gICAgZGVzaXJlZEFjY2VzczogQXJyYXk8SUFjY2Vzc1NwZWM+LFxuICAgIHJlc291cmNlQXJuczogQXJyYXk8c3RyaW5nPixcbiAgICB1c2VQYXNjYWxDYXNlOiBib29sZWFuID0gZmFsc2UpOiBBcnJheTxQb2xpY3lTdGF0ZW1lbnQ+IHtcbiAgICBsZXQgcG9saWN5U3RhdGVtZW50cyA9IG5ldyBBcnJheTxQb2xpY3lTdGF0ZW1lbnQ+KCk7XG4gICAgbGV0IGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5UmVjcyA9IHRoaXMubWVyZ2VEZXNpcmVkQWNjZXNzU3BlY3NCeUNhcGFiaWxpdHkoc3VwcG9ydGVkQ2FwYWJpbGl0aWVzLCBkZXNpcmVkQWNjZXNzKTtcbiAgICBsZXQgYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHk6IE1hcDxBY2Nlc3NDYXBhYmlsaXR5LCBJQWNjZXNzU3BlYz4gPSBuZXcgTWFwKCk7XG5cbiAgICBmb3IgKGxldCBbY2FwYWJpbGl0eVN0ciwgYWNjZXNzU3BlY10gb2YgT2JqZWN0LmVudHJpZXMoYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHlSZWNzKSkge1xuICAgICAgYWNjZXNzU3BlY3NCeUNhcGFiaWxpdHkuc2V0KGdldEFjY2Vzc0NhcGFiaWxpdHlGcm9tVmFsdWUoY2FwYWJpbGl0eVN0ciksIGFjY2Vzc1NwZWMpO1xuICAgIH1cblxuICAgIC8vIG9rLCB0aW1lIHRvIGFjdHVhbGx5IG1ha2UgQWxsb3cgU3RhdGVtZW50cyBmcm9tIG91ciBBY2Nlc3NTcGVjc1xuICAgIGZvciAobGV0IHN1cHBvcnRlZENhcGFiaWxpdHkgb2Ygc3VwcG9ydGVkQ2FwYWJpbGl0aWVzKSB7XG5cbiAgICAgIGxldCBhY2Nlc3NTcGVjOiBJQWNjZXNzU3BlYyA9IGFjY2Vzc1NwZWNzQnlDYXBhYmlsaXR5LmdldChzdXBwb3J0ZWRDYXBhYmlsaXR5KSB8fFxuICAgICAgICAgICAgICAgIHsgLy9zYXRpc2Z5IGNvbXBpbGVyOyBzaG91bGQgbmV2ZXIgaGFwcGVuLCBiZWNhdXNlIHdlIHBvcHVsYXRlIGF0IHRoZSBiZWdpbm5pbmcuXG4gICAgICAgICAgICAgICAgICAvL2dlbmVyYXRlIGEgZGVmYXVsdCBhY2Nlc3Mgc3BlYyBpZiBub25lIHdhcyBwcm92aWRlZFxuICAgICAgICAgICAgICAgICAgYWNjZXNzQ2FwYWJpbGl0aWVzOiBbc3VwcG9ydGVkQ2FwYWJpbGl0eV0sXG4gICAgICAgICAgICAgICAgICBhbGxvd1ByaW5jaXBhbEFybnM6IG5ldyBBcnJheTxzdHJpbmc+KCksXG4gICAgICAgICAgICAgICAgICB0ZXN0OiAnQXJuRXF1YWxzJyxcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICA7XG5cbiAgICAgIGxldCBhcm5Db25kaXRpb25UZXN0ID0gYWNjZXNzU3BlYy50ZXN0IHx8ICdBcm5FcXVhbHMnO1xuXG4gICAgICBsZXQgc2lkID0gYEFsbG93IFJlc3RyaWN0ZWQgJHtzdXBwb3J0ZWRDYXBhYmlsaXR5fWA7XG4gICAgICBpZiAodXNlUGFzY2FsQ2FzZSkge1xuICAgICAgICBzaWQgPSB0b1Bhc2NhbENhc2Uoc2lkKTtcbiAgICAgIH1cblxuICAgICAgbGV0IHN0YXRlbWVudCA9IHRoaXMubWFrZUFsbG93U3RhdGVtZW50KHNpZCxcbiAgICAgICAgdGhpcy5nZXRBY3Rpb25zKHNlcnZpY2VOYW1lLCBzdXBwb3J0ZWRDYXBhYmlsaXR5KSxcbiAgICAgICAgYWNjZXNzU3BlYy5hbGxvd1ByaW5jaXBhbEFybnMsXG4gICAgICAgIGFybkNvbmRpdGlvblRlc3QsXG4gICAgICAgIHJlc291cmNlQXJucyxcbiAgICAgICAgYWNjZXNzU3BlYy5yZXN0cmljdFRvUHJpbmNpcGFsT3JnSURzKTtcbiAgICAgIHBvbGljeVN0YXRlbWVudHMucHVzaChzdGF0ZW1lbnQpO1xuICAgIH1cbiAgICByZXR1cm4gcG9saWN5U3RhdGVtZW50cztcbiAgfVxuXG4gIG1ha2VBbGxvd1N0YXRlbWVudChzaWQ6IHN0cmluZyxcbiAgICBhY3Rpb25zOiBBcnJheTxzdHJpbmc+LFxuICAgIHByaW5jaXBhbEFybnM6IEFycmF5PHN0cmluZz4sXG4gICAgdGVzdDogQXJuQ29uZGl0aW9uVGVzdCxcbiAgICByZXNvdXJjZXM6IEFycmF5PHN0cmluZz4sXG4gICAgcmVzdHJpY3RUb1ByaW5jaXBhbE9yZ0lEcz86IEFycmF5PHN0cmluZz4pOiBQb2xpY3lTdGF0ZW1lbnQge1xuICAgIGNvbnN0IHBvbGljeVN0YXRlbWVudFByb3BzOiBQb2xpY3lTdGF0ZW1lbnRQcm9wcyA9IHtcbiAgICAgIHNpZDogc2lkLFxuICAgICAgZWZmZWN0OiBFZmZlY3QuQUxMT1csXG4gICAgfTtcbiAgICBjb25zdCBzdGF0ZW1lbnQgPSBuZXcgUG9saWN5U3RhdGVtZW50KHBvbGljeVN0YXRlbWVudFByb3BzKTtcbiAgICBzdGF0ZW1lbnQuYWRkQWN0aW9ucyguLi5hY3Rpb25zKTtcbiAgICBzdGF0ZW1lbnQuYWRkQW55UHJpbmNpcGFsKCk7XG4gICAgc3RhdGVtZW50LmFkZFJlc291cmNlcyguLi5yZXNvdXJjZXMpO1xuXG4gICAgY29uc3QgaXNXaWxkY2FyZFByaW5jaXBhbCA9IHByaW5jaXBhbEFybnMuaW5jbHVkZXMoJyonKTtcbiAgICBjb25zdCBoYXNPcmdDb25zdHJhaW50ID0gcmVzdHJpY3RUb1ByaW5jaXBhbE9yZ0lEcyAmJiByZXN0cmljdFRvUHJpbmNpcGFsT3JnSURzLmxlbmd0aCA+IDA7XG5cbiAgICBpZiAoaXNXaWxkY2FyZFByaW5jaXBhbCAmJiBoYXNPcmdDb25zdHJhaW50KSB7XG4gICAgICAvLyBDb2RlIFBhdGggQjogd2lsZGNhcmQgKyBvcmcgY29uc3RyYWludFxuICAgICAgLy8gVXNlIFByaW5jaXBhbDogXCIqXCIgKGFscmVhZHkgYWRkZWQgdmlhIGFkZEFueVByaW5jaXBhbCkgKyBhd3M6UHJpbmNpcGFsT3JnSUQgY29uZGl0aW9uXG4gICAgICAvLyBEbyBOT1QgYWRkIGF3czpQcmluY2lwYWxBcm4gY29uZGl0aW9uXG4gICAgICBzdGF0ZW1lbnQuYWRkQ29uZGl0aW9uKCdTdHJpbmdFcXVhbHMnLCB7ICdhd3M6UHJpbmNpcGFsT3JnSUQnOiByZXN0cmljdFRvUHJpbmNpcGFsT3JnSURzIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBDb2RlIFBhdGggQTogc3BlY2lmaWMgcHJpbmNpcGFsIEFSTnMgKGV4aXN0aW5nIGJlaGF2aW9yKVxuICAgICAgc3RhdGVtZW50LmFkZENvbmRpdGlvbih0ZXN0LCB7ICdhd3M6UHJpbmNpcGFsQXJuJzogSzlQb2xpY3lGYWN0b3J5LmRlZHVwbGljYXRlUHJpbmNpcGFscyhwcmluY2lwYWxBcm5zKSB9KTtcbiAgICAgIGlmIChoYXNPcmdDb25zdHJhaW50KSB7XG4gICAgICAgIC8vIFNwZWNpZmljIEFSTnMgKyBvcmcgY29uc3RyYWludDogYm90aCBjb25kaXRpb25zIG11c3QgYmUgdHJ1ZVxuICAgICAgICBzdGF0ZW1lbnQuYWRkQ29uZGl0aW9uKCdTdHJpbmdFcXVhbHMnLCB7ICdhd3M6UHJpbmNpcGFsT3JnSUQnOiByZXN0cmljdFRvUHJpbmNpcGFsT3JnSURzIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBzdGF0ZW1lbnQ7XG4gIH1cblxuICB3YXNMaWtlVXNlZChhY2Nlc3NTcGVjczogSUFjY2Vzc1NwZWNbXSk6IGJvb2xlYW4ge1xuICAgIGZvciAobGV0IGFjY2Vzc1NwZWMgb2YgYWNjZXNzU3BlY3MpIHtcbiAgICAgIGlmICgnQXJuTGlrZScgPT0gYWNjZXNzU3BlYy50ZXN0KSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBnZXRBbGxvd2VkUHJpbmNpcGFsQXJucyhhY2Nlc3NTcGVjczogSUFjY2Vzc1NwZWNbXSk6IEFycmF5PHN0cmluZz4ge1xuICAgIGxldCBhbGxvd2VkUHJpbmNpcGFsQXJucyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICAgIGZvciAobGV0IGFjY2Vzc1NwZWMgb2YgYWNjZXNzU3BlY3MpIHtcbiAgICAgIGFjY2Vzc1NwZWMuYWxsb3dQcmluY2lwYWxBcm5zLmZvckVhY2goZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIGFsbG93ZWRQcmluY2lwYWxBcm5zLmFkZCh2YWx1ZSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIEFycmF5LmZyb20oYWxsb3dlZFByaW5jaXBhbEFybnMpO1xuICB9XG5cbiAgLyoqXG4gICAgICogazkgd2FudHMgdG8gZGVueSBhbGwgQVdTIGFjY291bnRzIGFuZCBJQU0gcHJpbmNpcGFscyBub3QgZXhwbGljaXRseSBhbGxvd2VkOyB0aGlzICpzaG91bGQqXG4gICAgICogYmUgc3RyYWlnaHRmb3J3YXJkLCBidXQgaXQgaXNuJ3QgYmVjYXVzZSBvZiB0aGUgd2F5IGF3cy1jZGsgbWVyZ2VzIGFuZCBtYW5pcHVsYXRlcyBQcmluY2lwYWxzLlxuICAgICAqIEByZXR1cm4gbGlzdCBvZiBwcmluY2lwYWxzIGZvciBhIERlbnlFdmVyeW9uZUVsc2Ugc3RhdGVtZW50XG4gICAgICovXG4gIG1ha2VEZW55RXZlcnlvbmVFbHNlUHJpbmNpcGFscygpOiBBcm5QcmluY2lwYWxbXSB7XG4gICAgLyoqXG4gICAgICAgICAqIFdlIHNob3VsZCBiZSBhYmxlIHRvIHByb3ZpZGUgQW55UHJpbmNpcGFsIG9uY2UgKG9mIGNvdXJzZSksIGJ1dCBBV1MgQ0RLIGNvbnZlcnRzOlxuICAgICAgICAgKiBcIlByaW5jaXBhbFwiOiB7XG4gICAgICAgICAqICAgXCJBV1NcIjogXCIqXCIgICAgLy8gaWRlbnRpZmllcyBhbGwgQVdTIGFjY291bnRzIGFuZCBJQU0uXG4gICAgICAgICAqIH1cbiAgICAgICAgICogdG86XG4gICAgICAgICAqIFwi