@k9securityio/k9-cdk
Version:
Provision strong AWS security policies easily using the AWS CDK.
355 lines • 53.4 kB
JavaScript
"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