UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

319 lines 50.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Bootstrapper = void 0; const console_1 = require("console"); const path = require("path"); const deploy_bootstrap_1 = require("./deploy-bootstrap"); const legacy_template_1 = require("./legacy-template"); const logging_1 = require("../../logging"); const serialize_1 = require("../../serialize"); const error_1 = require("../../toolkit/error"); const directories_1 = require("../../util/directories"); const mode_1 = require("../plugin/mode"); class Bootstrapper { constructor(source = { source: 'default' }) { this.source = source; } bootstrapEnvironment(environment, sdkProvider, options = {}) { switch (this.source.source) { case 'legacy': return this.legacyBootstrap(environment, sdkProvider, options); case 'default': return this.modernBootstrap(environment, sdkProvider, options); case 'custom': return this.customBootstrap(environment, sdkProvider, options); } } async showTemplate(json) { const template = await this.loadTemplate(); process.stdout.write(`${(0, serialize_1.serializeStructure)(template, json)}\n`); } /** * Deploy legacy bootstrap stack * */ async legacyBootstrap(environment, sdkProvider, options = {}) { const params = options.parameters ?? {}; if (params.trustedAccounts?.length) { throw new error_1.ToolkitError('--trust can only be passed for the modern bootstrap experience.'); } if (params.cloudFormationExecutionPolicies?.length) { throw new error_1.ToolkitError('--cloudformation-execution-policies can only be passed for the modern bootstrap experience.'); } if (params.createCustomerMasterKey !== undefined) { throw new error_1.ToolkitError('--bootstrap-customer-key can only be passed for the modern bootstrap experience.'); } if (params.qualifier) { throw new error_1.ToolkitError('--qualifier can only be passed for the modern bootstrap experience.'); } const current = await deploy_bootstrap_1.BootstrapStack.lookup(sdkProvider, environment, options.toolkitStackName); return current.update(await this.loadTemplate(params), {}, { ...options, terminationProtection: options.terminationProtection ?? current.terminationProtection, }); } /** * Deploy CI/CD-ready bootstrap stack from template * */ async modernBootstrap(environment, sdkProvider, options = {}) { const params = options.parameters ?? {}; const bootstrapTemplate = await this.loadTemplate(); const current = await deploy_bootstrap_1.BootstrapStack.lookup(sdkProvider, environment, options.toolkitStackName); const partition = await current.partition(); if (params.createCustomerMasterKey !== undefined && params.kmsKeyId) { throw new error_1.ToolkitError("You cannot pass '--bootstrap-kms-key-id' and '--bootstrap-customer-key' together. Specify one or the other"); } // If people re-bootstrap, existing parameter values are reused so that people don't accidentally change the configuration // on their bootstrap stack (this happens automatically in deployStack). However, to do proper validation on the // combined arguments (such that if --trust has been given, --cloudformation-execution-policies is necessary as well) // we need to take this parameter reuse into account. // // Ideally we'd do this inside the template, but the `Rules` section of CFN // templates doesn't seem to be able to express the conditions that we need // (can't use Fn::Join or reference Conditions) so we do it here instead. const allTrusted = new Set([ ...params.trustedAccounts ?? [], ...params.trustedAccountsForLookup ?? [], ]); const invalid = intersection(allTrusted, new Set(params.untrustedAccounts)); if (invalid.size > 0) { throw new error_1.ToolkitError(`Accounts cannot be both trusted and untrusted. Found: ${[...invalid].join(',')}`); } const removeUntrusted = (accounts) => accounts.filter(acc => !params.untrustedAccounts?.map(String).includes(String(acc))); const trustedAccounts = removeUntrusted(params.trustedAccounts ?? splitCfnArray(current.parameters.TrustedAccounts)); (0, console_1.info)(`Trusted accounts for deployment: ${trustedAccounts.length > 0 ? trustedAccounts.join(', ') : '(none)'}`); const trustedAccountsForLookup = removeUntrusted(params.trustedAccountsForLookup ?? splitCfnArray(current.parameters.TrustedAccountsForLookup)); (0, console_1.info)(`Trusted accounts for lookup: ${trustedAccountsForLookup.length > 0 ? trustedAccountsForLookup.join(', ') : '(none)'}`); const cloudFormationExecutionPolicies = params.cloudFormationExecutionPolicies ?? splitCfnArray(current.parameters.CloudFormationExecutionPolicies); if (trustedAccounts.length === 0 && cloudFormationExecutionPolicies.length === 0) { // For self-trust it's okay to default to AdministratorAccess, and it improves the usability of bootstrapping a lot. // // We don't actually make the implicitly policy a physical parameter. The template will infer it instead, // we simply do the UI advertising that behavior here. // // If we DID make it an explicit parameter, we wouldn't be able to tell the difference between whether // we inferred it or whether the user told us, and the sequence: // // $ cdk bootstrap // $ cdk bootstrap --trust 1234 // // Would leave AdministratorAccess policies with a trust relationship, without the user explicitly // approving the trust policy. const implicitPolicy = `arn:${partition}:iam::aws:policy/AdministratorAccess`; (0, logging_1.warning)(`Using default execution policy of '${implicitPolicy}'. Pass '--cloudformation-execution-policies' to customize.`); } else if (cloudFormationExecutionPolicies.length === 0) { throw new error_1.ToolkitError(`Please pass \'--cloudformation-execution-policies\' when using \'--trust\' to specify deployment permissions. Try a managed policy of the form \'arn:${partition}:iam::aws:policy/<PolicyName>\'.`); } else { // Remind people what the current settings are (0, console_1.info)(`Execution policies: ${cloudFormationExecutionPolicies.join(', ')}`); } // * If an ARN is given, that ARN. Otherwise: // * '-' if customerKey = false // * '' if customerKey = true // * if customerKey is also not given // * undefined if we already had a value in place (reusing what we had) // * '-' if this is the first time we're deploying this stack (or upgrading from old to new bootstrap) const currentKmsKeyId = current.parameters.FileAssetsBucketKmsKeyId; const kmsKeyId = params.kmsKeyId ?? (params.createCustomerMasterKey === true ? CREATE_NEW_KEY : params.createCustomerMasterKey === false || currentKmsKeyId === undefined ? USE_AWS_MANAGED_KEY : undefined); /* A permissions boundary can be provided via: * - the flag indicating the example one should be used * - the name indicating the custom permissions boundary to be used * Re-bootstrapping will NOT be blocked by either tightening or relaxing the permissions' boundary. */ // InputPermissionsBoundary is an `any` type and if it is not defined it // appears as an empty string ''. We need to force it to evaluate an empty string // as undefined const currentPermissionsBoundary = current.parameters.InputPermissionsBoundary || undefined; const inputPolicyName = params.examplePermissionsBoundary ? CDK_BOOTSTRAP_PERMISSIONS_BOUNDARY : params.customPermissionsBoundary; let policyName; if (inputPolicyName) { // If the example policy is not already in place, it must be created. const sdk = (await sdkProvider.forEnvironment(environment, mode_1.Mode.ForWriting)).sdk; policyName = await this.getPolicyName(environment, sdk, inputPolicyName, partition, params); } if (currentPermissionsBoundary !== policyName) { if (!currentPermissionsBoundary) { (0, logging_1.warning)(`Adding new permissions boundary ${policyName}`); } else if (!policyName) { (0, logging_1.warning)(`Removing existing permissions boundary ${currentPermissionsBoundary}`); } else { (0, logging_1.warning)(`Changing permissions boundary from ${currentPermissionsBoundary} to ${policyName}`); } } return current.update(bootstrapTemplate, { FileAssetsBucketName: params.bucketName, FileAssetsBucketKmsKeyId: kmsKeyId, // Empty array becomes empty string TrustedAccounts: trustedAccounts.join(','), TrustedAccountsForLookup: trustedAccountsForLookup.join(','), CloudFormationExecutionPolicies: cloudFormationExecutionPolicies.join(','), Qualifier: params.qualifier, PublicAccessBlockConfiguration: params.publicAccessBlockConfiguration || params.publicAccessBlockConfiguration === undefined ? 'true' : 'false', InputPermissionsBoundary: policyName, }, { ...options, terminationProtection: options.terminationProtection ?? current.terminationProtection, }); } async getPolicyName(environment, sdk, permissionsBoundary, partition, params) { if (permissionsBoundary !== CDK_BOOTSTRAP_PERMISSIONS_BOUNDARY) { this.validatePolicyName(permissionsBoundary); return Promise.resolve(permissionsBoundary); } // if no Qualifier is supplied, resort to the default one const arn = await this.getExamplePermissionsBoundary(params.qualifier ?? 'hnb659fds', partition, environment.account, sdk); const policyName = arn.split('/').pop(); if (!policyName) { throw new error_1.ToolkitError('Could not retrieve the example permission boundary!'); } return Promise.resolve(policyName); } async getExamplePermissionsBoundary(qualifier, partition, account, sdk) { const iam = sdk.iam(); let policyName = `cdk-${qualifier}-permissions-boundary`; const arn = `arn:${partition}:iam::${account}:policy/${policyName}`; try { let getPolicyResp = await iam.getPolicy({ PolicyArn: arn }); if (getPolicyResp.Policy) { return arn; } } catch (e) { // https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicy.html#API_GetPolicy_Errors if (e.name === 'NoSuchEntity') { //noop, proceed with creating the policy } else { throw e; } } const policyDoc = { Version: '2012-10-17', Statement: [ { Action: ['*'], Resource: '*', Effect: 'Allow', Sid: 'ExplicitAllowAll', }, { Condition: { StringEquals: { 'iam:PermissionsBoundary': `arn:${partition}:iam::${account}:policy/cdk-${qualifier}-permissions-boundary`, }, }, Action: [ 'iam:CreateUser', 'iam:CreateRole', 'iam:PutRolePermissionsBoundary', 'iam:PutUserPermissionsBoundary', ], Resource: '*', Effect: 'Allow', Sid: 'DenyAccessIfRequiredPermBoundaryIsNotBeingApplied', }, { Action: [ 'iam:CreatePolicyVersion', 'iam:DeletePolicy', 'iam:DeletePolicyVersion', 'iam:SetDefaultPolicyVersion', ], Resource: `arn:${partition}:iam::${account}:policy/cdk-${qualifier}-permissions-boundary`, Effect: 'Deny', Sid: 'DenyPermBoundaryIAMPolicyAlteration', }, { Action: ['iam:DeleteUserPermissionsBoundary', 'iam:DeleteRolePermissionsBoundary'], Resource: '*', Effect: 'Deny', Sid: 'DenyRemovalOfPermBoundaryFromAnyUserOrRole', }, ], }; const request = { PolicyName: policyName, PolicyDocument: JSON.stringify(policyDoc), }; const createPolicyResponse = await iam.createPolicy(request); if (createPolicyResponse.Policy?.Arn) { return createPolicyResponse.Policy.Arn; } else { throw new error_1.ToolkitError(`Could not retrieve the example permission boundary ${arn}!`); } } validatePolicyName(permissionsBoundary) { // https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreatePolicy.html // Added support for policy names with a path // See https://github.com/aws/aws-cdk/issues/26320 const regexp = /[\w+\/=,.@-]+/; const matches = regexp.exec(permissionsBoundary); if (!(matches && matches.length === 1 && matches[0] === permissionsBoundary)) { throw new error_1.ToolkitError(`The permissions boundary name ${permissionsBoundary} does not match the IAM conventions.`); } } async customBootstrap(environment, sdkProvider, options = {}) { // Look at the template, decide whether it's most likely a legacy or modern bootstrap // template, and use the right bootstrapper for that. const version = (0, deploy_bootstrap_1.bootstrapVersionFromTemplate)(await this.loadTemplate()); if (version === 0) { return this.legacyBootstrap(environment, sdkProvider, options); } else { return this.modernBootstrap(environment, sdkProvider, options); } } async loadTemplate(params = {}) { switch (this.source.source) { case 'custom': return (0, serialize_1.loadStructuredFile)(this.source.templateFile); case 'default': return (0, serialize_1.loadStructuredFile)(path.join((0, directories_1.rootDir)(), 'lib', 'api', 'bootstrap', 'bootstrap-template.yaml')); case 'legacy': return (0, legacy_template_1.legacyBootstrapTemplate)(params); } } } exports.Bootstrapper = Bootstrapper; /** * Magic parameter value that will cause the bootstrap-template.yml to NOT create a CMK but use the default key */ const USE_AWS_MANAGED_KEY = 'AWS_MANAGED_KEY'; /** * Magic parameter value that will cause the bootstrap-template.yml to create a CMK */ const CREATE_NEW_KEY = ''; /** * Parameter value indicating the use of the default, CDK provided permissions boundary for bootstrap-template.yml */ const CDK_BOOTSTRAP_PERMISSIONS_BOUNDARY = 'CDK_BOOTSTRAP_PERMISSIONS_BOUNDARY'; /** * Split an array-like CloudFormation parameter on , * * An empty string is the empty array (instead of `['']`). */ function splitCfnArray(xs) { if (xs === '' || xs === undefined) { return []; } return xs.split(','); } function intersection(xs, ys) { return new Set(Array.from(xs).filter(x => ys.has(x))); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9vdHN0cmFwLWVudmlyb25tZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYm9vdHN0cmFwLWVudmlyb25tZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFDQUErQjtBQUMvQiw2QkFBNkI7QUFHN0IseURBQWtGO0FBQ2xGLHVEQUE0RDtBQUM1RCwyQ0FBd0M7QUFDeEMsK0NBQXlFO0FBQ3pFLCtDQUFtRDtBQUNuRCx3REFBaUQ7QUFHakQseUNBQXNDO0FBSXRDLE1BQWEsWUFBWTtJQUN2QixZQUE2QixTQUEwQixFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUU7UUFBL0MsV0FBTSxHQUFOLE1BQU0sQ0FBeUM7SUFBRyxDQUFDO0lBRXpFLG9CQUFvQixDQUN6QixXQUE4QixFQUM5QixXQUF3QixFQUN4QixVQUF1QyxFQUFFO1FBRXpDLFFBQVEsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUMzQixLQUFLLFFBQVE7Z0JBQ1gsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDLFdBQVcsRUFBRSxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDakUsS0FBSyxTQUFTO2dCQUNaLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQyxXQUFXLEVBQUUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ2pFLEtBQUssUUFBUTtnQkFDWCxPQUFPLElBQUksQ0FBQyxlQUFlLENBQUMsV0FBVyxFQUFFLFdBQVcsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNuRSxDQUFDO0lBQ0gsQ0FBQztJQUVNLEtBQUssQ0FBQyxZQUFZLENBQUMsSUFBYTtRQUNyQyxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUMzQyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLElBQUEsOEJBQWtCLEVBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBRUQ7OztP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QUMxQjs7R0FFRztBQUNILE1BQU0sa0NBQWtDLEdBQUcsb0NBQW9DLENBQUM7QUFFaEY7Ozs7R0FJRztBQUNILFNBQVMsYUFBYSxDQUFDLEVBQXNCO0lBQzNDLElBQUksRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssU0FBUyxFQUFFLENBQUM7UUFDbEMsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0lBQ0QsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0FBQ3ZCLENBQUM7QUFFRCxTQUFTLFlBQVksQ0FBSSxFQUFVLEVBQUUsRUFBVTtJQUM3QyxPQUFPLElBQUksR0FBRyxDQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDM0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGluZm8gfSBmcm9tICdjb25zb2xlJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBjeGFwaSBmcm9tICdAYXdzLWNkay9jeC1hcGknO1xuaW1wb3J0IHR5cGUgeyBCb290c3RyYXBFbnZpcm9ubWVudE9wdGlvbnMsIEJvb3RzdHJhcHBpbmdQYXJhbWV0ZXJzIH0gZnJvbSAnLi9ib290c3RyYXAtcHJvcHMnO1xuaW1wb3J0IHsgQm9vdHN0cmFwU3RhY2ssIGJvb3RzdHJhcFZlcnNpb25Gcm9tVGVtcGxhdGUgfSBmcm9tICcuL2RlcGxveS1ib290c3RyYXAnO1xuaW1wb3J0IHsgbGVnYWN5Qm9vdHN0cmFwVGVtcGxhdGUgfSBmcm9tICcuL2xlZ2FjeS10ZW1wbGF0ZSc7XG5pbXBvcnQgeyB3YXJuaW5nIH0gZnJvbSAnLi4vLi4vbG9nZ2luZyc7XG5pbXBvcnQgeyBsb2FkU3RydWN0dXJlZEZpbGUsIHNlcmlhbGl6ZVN0cnVjdHVyZSB9IGZyb20gJy4uLy4uL3NlcmlhbGl6ZSc7XG5pbXBvcnQgeyBUb29sa2l0RXJyb3IgfSBmcm9tICcuLi8uLi90b29sa2l0L2Vycm9yJztcbmltcG9ydCB7IHJvb3REaXIgfSBmcm9tICcuLi8uLi91dGlsL2RpcmVjdG9yaWVzJztcbmltcG9ydCB0eXBlIHsgU0RLLCBTZGtQcm92aWRlciB9IGZyb20gJy4uL2F3cy1hdXRoJztcbmltcG9ydCB0eXBlIHsgU3VjY2Vzc2Z1bERlcGxveVN0YWNrUmVzdWx0IH0gZnJvbSAnLi4vZGVwbG95bWVudHMnO1xuaW1wb3J0IHsgTW9kZSB9IGZyb20gJy4uL3BsdWdpbi9tb2RlJztcblxuZXhwb3J0IHR5cGUgQm9vdHN0cmFwU291cmNlID0geyBzb3VyY2U6ICdsZWdhY3knIH0gfCB7IHNvdXJjZTogJ2RlZmF1bHQnIH0gfCB7IHNvdXJjZTogJ2N1c3RvbSc7IHRlbXBsYXRlRmlsZTogc3RyaW5nIH07XG5cbmV4cG9ydCBjbGFzcyBCb290c3RyYXBwZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IHNvdXJjZTogQm9vdHN0cmFwU291cmNlID0geyBzb3VyY2U6ICdkZWZhdWx0JyB9KSB7fVxuXG4gIHB1YmxpYyBib290c3RyYXBFbnZpcm9ubWVudChcbiAgICBlbnZpcm9ubWVudDogY3hhcGkuRW52aXJvbm1lbnQsXG4gICAgc2RrUHJvdmlkZXI6IFNka1Byb3ZpZGVyLFxuICAgIG9wdGlvbnM6IEJvb3RzdHJhcEVudmlyb25tZW50T3B0aW9ucyA9IHt9LFxuICApOiBQcm9taXNlPFN1Y2Nlc3NmdWxEZXBsb3lTdGFja1Jlc3VsdD4ge1xuICAgIHN3aXRjaCAodGhpcy5zb3VyY2Uuc291cmNlKSB7XG4gICAgICBjYXNlICdsZWdhY3knOlxuICAgICAgICByZXR1cm4gdGhpcy5sZWdhY3lCb290c3RyYXAoZW52aXJvbm1lbnQsIHNka1Byb3ZpZGVyLCBvcHRpb25zKTtcbiAgICAgIGNhc2UgJ2RlZmF1bHQnOlxuICAgICAgICByZXR1cm4gdGhpcy5tb2Rlcm5Cb290c3RyYXAoZW52aXJvbm1lbnQsIHNka1Byb3ZpZGVyLCBvcHRpb25zKTtcbiAgICAgIGNhc2UgJ2N1c3RvbSc6XG4gICAgICAgIHJldHVybiB0aGlzLmN1c3RvbUJvb3RzdHJhcChlbnZpcm9ubWVudCwgc2RrUHJvdmlkZXIsIG9wdGlvbnMpO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBzaG93VGVtcGxhdGUoanNvbjogYm9vbGVhbikge1xuICAgIGNvbnN0IHRlbXBsYXRlID0gYXdhaXQgdGhpcy5sb2FkVGVtcGxhdGUoKTtcbiAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShgJHtzZXJpYWxpemVTdHJ1Y3R1cmUodGVtcGxhdGUsIGpzb24pfVxcbmApO1xuICB9XG5cbiAgLyoqXG4gICAqIERlcGxveSBsZWdhY3kgYm9vdHN0cmFwIHN0YWNrXG4gICAqXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGxlZ2FjeUJvb3RzdHJhcChcbiAgICBlbnZpcm9ubWVudDogY3hhcGkuRW52aXJvbm1lbnQsXG4gICAgc2RrUHJvdmlkZXI6IFNka1Byb3ZpZGVyLFxuICAgIG9wdGlvbnM6IEJvb3RzdHJhcEVudmlyb25tZW50T3B0aW9ucyA9IHt9LFxuICApOiBQcm9taXNlPFN1Y2Nlc3NmdWxEZXBsb3lTdGFja1Jlc3VsdD4ge1xuICAgIGNvbnN0IHBhcmFtcyA9IG9wdGlvbnMucGFyYW1ldGVycyA/PyB7fTtcblxuICAgIGlmIChwYXJhbXMudHJ1c3RlZEFjY291bnRzPy5sZW5ndGgpIHtcbiAgICAgIHRocm93IG5ldyBUb29sa2l0RXJyb3IoJy0tdHJ1c3QgY2FuIG9ubHkgYmUgcGFzc2VkIGZvciB0aGUgbW9kZXJuIGJvb3RzdHJhcCBleHBlcmllbmNlLicpO1xuICAgIH1cbiAgICBpZiAocGFyYW1zLmNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXM/Lmxlbmd0aCkge1xuICAgICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcignLS1jbG91ZGZvcm1hdGlvbi1leGVjdXRpb24tcG9saWNpZXMgY2FuIG9ubHkgYmUgcGFzc2VkIGZvciB0aGUgbW9kZXJuIGJvb3RzdHJhcCBleHBlcmllbmNlLicpO1xuICAgIH1cbiAgICBpZiAocGFyYW1zLmNyZWF0ZUN1c3RvbWVyTWFzdGVyS2V5ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBUb29sa2l0RXJyb3IoJy0tYm9vdHN0cmFwLWN1c3RvbWVyLWtleSBjYW4gb25seSBiZSBwYXNzZWQgZm9yIHRoZSBtb2Rlcm4gYm9vdHN0cmFwIGV4cGVyaWVuY2UuJyk7XG4gICAgfVxuICAgIGlmIChwYXJhbXMucXVhbGlmaWVyKSB7XG4gICAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKCctLXF1YWxpZmllciBjYW4gb25seSBiZSBwYXNzZWQgZm9yIHRoZSBtb2Rlcm4gYm9vdHN0cmFwIGV4cGVyaWVuY2UuJyk7XG4gICAgfVxuXG4gICAgY29uc3QgY3VycmVudCA9IGF3YWl0IEJvb3RzdHJhcFN0YWNrLmxvb2t1cChzZGtQcm92aWRlciwgZW52aXJvbm1lbnQsIG9wdGlvbnMudG9vbGtpdFN0YWNrTmFtZSk7XG4gICAgcmV0dXJuIGN1cnJlbnQudXBkYXRlKFxuICAgICAgYXdhaXQgdGhpcy5sb2FkVGVtcGxhdGUocGFyYW1zKSxcbiAgICAgIHt9LFxuICAgICAge1xuICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICB0ZXJtaW5hdGlvblByb3RlY3Rpb246IG9wdGlvbnMudGVybWluYXRpb25Qcm90ZWN0aW9uID8/IGN1cnJlbnQudGVybWluYXRpb25Qcm90ZWN0aW9uLFxuICAgICAgfSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIERlcGxveSBDSS9DRC1yZWFkeSBib290c3RyYXAgc3RhY2sgZnJvbSB0ZW1wbGF0ZVxuICAgKlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBtb2Rlcm5Cb290c3RyYXAoXG4gICAgZW52aXJvbm1lbnQ6IGN4YXBpLkVudmlyb25tZW50LFxuICAgIHNka1Byb3ZpZGVyOiBTZGtQcm92aWRlcixcbiAgICBvcHRpb25zOiBCb290c3RyYXBFbnZpcm9ubWVudE9wdGlvbnMgPSB7fSxcbiAgKTogUHJvbWlzZTxTdWNjZXNzZnVsRGVwbG95U3RhY2tSZXN1bHQ+IHtcbiAgICBjb25zdCBwYXJhbXMgPSBvcHRpb25zLnBhcmFtZXRlcnMgPz8ge307XG5cbiAgICBjb25zdCBib290c3RyYXBUZW1wbGF0ZSA9IGF3YWl0IHRoaXMubG9hZFRlbXBsYXRlKCk7XG5cbiAgICBjb25zdCBjdXJyZW50ID0gYXdhaXQgQm9vdHN0cmFwU3RhY2subG9va3VwKHNka1Byb3ZpZGVyLCBlbnZpcm9ubWVudCwgb3B0aW9ucy50b29sa2l0U3RhY2tOYW1lKTtcbiAgICBjb25zdCBwYXJ0aXRpb24gPSBhd2FpdCBjdXJyZW50LnBhcnRpdGlvbigpO1xuXG4gICAgaWYgKHBhcmFtcy5jcmVhdGVDdXN0b21lck1hc3RlcktleSAhPT0gdW5kZWZpbmVkICYmIHBhcmFtcy5rbXNLZXlJZCkge1xuICAgICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcihcbiAgICAgICAgXCJZb3UgY2Fubm90IHBhc3MgJy0tYm9vdHN0cmFwLWttcy1rZXktaWQnIGFuZCAnLS1ib290c3RyYXAtY3VzdG9tZXIta2V5JyB0b2dldGhlci4gU3BlY2lmeSBvbmUgb3IgdGhlIG90aGVyXCIsXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIElmIHBlb3BsZSByZS1ib290c3RyYXAsIGV4aXN0aW5nIHBhcmFtZXRlciB2YWx1ZXMgYXJlIHJldXNlZCBzbyB0aGF0IHBlb3BsZSBkb24ndCBhY2NpZGVudGFsbHkgY2hhbmdlIHRoZSBjb25maWd1cmF0aW9uXG4gICAgLy8gb24gdGhlaXIgYm9vdHN0cmFwIHN0YWNrICh0aGlzIGhhcHBlbnMgYXV0b21hdGljYWxseSBpbiBkZXBsb3lTdGFjaykuIEhvd2V2ZXIsIHRvIGRvIHByb3BlciB2YWxpZGF0aW9uIG9uIHRoZVxuICAgIC8vIGNvbWJpbmVkIGFyZ3VtZW50cyAoc3VjaCB0aGF0IGlmIC0tdHJ1c3QgaGFzIGJlZW4gZ2l2ZW4sIC0tY2xvdWRmb3JtYXRpb24tZXhlY3V0aW9uLXBvbGljaWVzIGlzIG5lY2Vzc2FyeSBhcyB3ZWxsKVxuICAgIC8vIHdlIG5lZWQgdG8gdGFrZSB0aGlzIHBhcmFtZXRlciByZXVzZSBpbnRvIGFjY291bnQuXG4gICAgLy9cbiAgICAvLyBJZGVhbGx5IHdlJ2QgZG8gdGhpcyBpbnNpZGUgdGhlIHRlbXBsYXRlLCBidXQgdGhlIGBSdWxlc2Agc2VjdGlvbiBvZiBDRk5cbiAgICAvLyB0ZW1wbGF0ZXMgZG9lc24ndCBzZWVtIHRvIGJlIGFibGUgdG8gZXhwcmVzcyB0aGUgY29uZGl0aW9ucyB0aGF0IHdlIG5lZWRcbiAgICAvLyAoY2FuJ3QgdXNlIEZuOjpKb2luIG9yIHJlZmVyZW5jZSBDb25kaXRpb25zKSBzbyB3ZSBkbyBpdCBoZXJlIGluc3RlYWQuXG4gICAgY29uc3QgYWxsVHJ1c3RlZCA9IG5ldyBTZXQoW1xuICAgICAgLi4ucGFyYW1zLnRydXN0ZWRBY2NvdW50cyA/PyBbXSxcbiAgICAgIC4uLnBhcmFtcy50cnVzdGVkQWNjb3VudHNGb3JMb29rdXAgPz8gW10sXG4gICAgXSk7XG4gICAgY29uc3QgaW52YWxpZCA9IGludGVyc2VjdGlvbihhbGxUcnVzdGVkLCBuZXcgU2V0KHBhcmFtcy51bnRydXN0ZWRBY2NvdW50cykpO1xuICAgIGlmIChpbnZhbGlkLnNpemUgPiAwKSB7XG4gICAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKGBBY2NvdW50cyBjYW5ub3QgYmUgYm90aCB0cnVzdGVkIGFuZCB1bnRydXN0ZWQuIEZvdW5kOiAke1suLi5pbnZhbGlkXS5qb2luKCcsJyl9YCk7XG4gICAgfVxuXG4gICAgY29uc3QgcmVtb3ZlVW50cnVzdGVkID0gKGFjY291bnRzOiBzdHJpbmdbXSkgPT5cbiAgICAgIGFjY291bnRzLmZpbHRlcihhY2MgPT4gIXBhcmFtcy51bnRydXN0ZWRBY2NvdW50cz8ubWFwKFN0cmluZykuaW5jbHVkZXMoU3RyaW5nKGFjYykpKTtcblxuICAgIGNvbnN0IHRydXN0ZWRBY2NvdW50cyA9IHJlbW92ZVVudHJ1c3RlZChwYXJhbXMudHJ1c3RlZEFjY291bnRzID8/IHNwbGl0Q2ZuQXJyYXkoY3VycmVudC5wYXJhbWV0ZXJzLlRydXN0ZWRBY2NvdW50cykpO1xuICAgIGluZm8oYFRydXN0ZWQgYWNjb3VudHMgZm9yIGRlcGxveW1lbnQ6ICR7dHJ1c3RlZEFjY291bnRzLmxlbmd0aCA+IDAgPyB0cnVzdGVkQWNjb3VudHMuam9pbignLCAnKSA6ICcobm9uZSknfWApO1xuXG4gICAgY29uc3QgdHJ1c3RlZEFjY291bnRzRm9yTG9va3VwID0gcmVtb3ZlVW50cnVzdGVkKFxuICAgICAgcGFyYW1zLnRydXN0ZWRBY2NvdW50c0Zvckxvb2t1cCA/PyBzcGxpdENmbkFycmF5KGN1cnJlbnQucGFyYW1ldGVycy5UcnVzdGVkQWNjb3VudHNGb3JMb29rdXApLFxuICAgICk7XG4gICAgaW5mbyhcbiAgICAgIGBUcnVzdGVkIGFjY291bnRzIGZvciBsb29rdXA6ICR7dHJ1c3RlZEFjY291bnRzRm9yTG9va3VwLmxlbmd0aCA+IDAgPyB0cnVzdGVkQWNjb3VudHNGb3JMb29rdXAuam9pbignLCAnKSA6ICcobm9uZSknfWAsXG4gICAgKTtcblxuICAgIGNvbnN0IGNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXMgPVxuICAgICAgcGFyYW1zLmNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXMgPz8gc3BsaXRDZm5BcnJheShjdXJyZW50LnBhcmFtZXRlcnMuQ2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llcyk7XG4gICAgaWYgKHRydXN0ZWRBY2NvdW50cy5sZW5ndGggPT09IDAgJiYgY2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llcy5sZW5ndGggPT09IDApIHtcbiAgICAgIC8vIEZvciBzZWxmLXRydXN0IGl0J3Mgb2theSB0byBkZWZhdWx0IHRvIEFkbWluaXN0cmF0b3JBY2Nlc3MsIGFuZCBpdCBpbXByb3ZlcyB0aGUgdXNhYmlsaXR5IG9mIGJvb3RzdHJhcHBpbmcgYSBsb3QuXG4gICAgICAvL1xuICAgICAgLy8gV2UgZG9uJ3QgYWN0dWFsbHkgbWFrZSB0aGUgaW1wbGljaXRseSBwb2xpY3kgYSBwaHlzaWNhbCBwYXJhbWV0ZXIuIFRoZSB0ZW1wbGF0ZSB3aWxsIGluZmVyIGl0IGluc3RlYWQsXG4gICAgICAvLyB3ZSBzaW1wbHkgZG8gdGhlIFVJIGFkdmVydGlzaW5nIHRoYXQgYmVoYXZpb3IgaGVyZS5cbiAgICAgIC8vXG4gICAgICAvLyBJZiB3ZSBESUQgbWFrZSBpdCBhbiBleHBsaWNpdCBwYXJhbWV0ZXIsIHdlIHdvdWxkbid0IGJlIGFibGUgdG8gdGVsbCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHdoZXRoZXJcbiAgICAgIC8vIHdlIGluZmVycmVkIGl0IG9yIHdoZXRoZXIgdGhlIHVzZXIgdG9sZCB1cywgYW5kIHRoZSBzZXF1ZW5jZTpcbiAgICAgIC8vXG4gICAgICAvLyAkIGNkayBib290c3RyYXBcbiAgICAgIC8vICQgY2RrIGJvb3RzdHJhcCAtLXRydXN0IDEyMzRcbiAgICAgIC8vXG4gICAgICAvLyBXb3VsZCBsZWF2ZSBBZG1pbmlzdHJhdG9yQWNjZXNzIHBvbGljaWVzIHdpdGggYSB0cnVzdCByZWxhdGlvbnNoaXAsIHdpdGhvdXQgdGhlIHVzZXIgZXhwbGljaXRseVxuICAgICAgLy8gYXBwcm92aW5nIHRoZSB0cnVzdCBwb2xpY3kuXG4gICAgICBjb25zdCBpbXBsaWNpdFBvbGljeSA9IGBhcm46JHtwYXJ0aXRpb259OmlhbTo6YXdzOnBvbGljeS9BZG1pbmlzdHJhdG9yQWNjZXNzYDtcbiAgICAgIHdhcm5pbmcoXG4gICAgICAgIGBVc2luZyBkZWZhdWx0IGV4ZWN1dGlvbiBwb2xpY3kgb2YgJyR7aW1wbGljaXRQb2xpY3l9Jy4gUGFzcyAnLS1jbG91ZGZvcm1hdGlvbi1leGVjdXRpb24tcG9saWNpZXMnIHRvIGN1c3RvbWl6ZS5gLFxuICAgICAgKTtcbiAgICB9IGVsc2UgaWYgKGNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKFxuICAgICAgICBgUGxlYXNlIHBhc3MgXFwnLS1jbG91ZGZvcm1hdGlvbi1leGVjdXRpb24tcG9saWNpZXNcXCcgd2hlbiB1c2luZyBcXCctLXRydXN0XFwnIHRvIHNwZWNpZnkgZGVwbG95bWVudCBwZXJtaXNzaW9ucy4gVHJ5IGEgbWFuYWdlZCBwb2xpY3kgb2YgdGhlIGZvcm0gXFwnYXJuOiR7cGFydGl0aW9ufTppYW06OmF3czpwb2xpY3kvPFBvbGljeU5hbWU+XFwnLmAsXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBSZW1pbmQgcGVvcGxlIHdoYXQgdGhlIGN1cnJlbnQgc2V0dGluZ3MgYXJlXG4gICAgICBpbmZvKGBFeGVjdXRpb24gcG9saWNpZXM6ICR7Y2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llcy5qb2luKCcsICcpfWApO1xuICAgIH1cblxuICAgIC8vICogSWYgYW4gQVJOIGlzIGdpdmVuLCB0aGF0IEFSTi4gT3RoZXJ3aXNlOlxuICAgIC8vICAgKiAnLScgaWYgY3VzdG9tZXJLZXkgPSBmYWxzZVxuICAgIC8vICAgKiAnJyBpZiBjdXN0b21lcktleSA9IHRydWVcbiAgICAvLyAgICogaWYgY3VzdG9tZXJLZXkgaXMgYWxzbyBub3QgZ2l2ZW5cbiAgICAvLyAgICAgKiB1bmRlZmluZWQgaWYgd2UgYWxyZWFkeSBoYWQgYSB2YWx1ZSBpbiBwbGFjZSAocmV1c2luZyB3aGF0IHdlIGhhZClcbiAgICAvLyAgICAgKiAnLScgaWYgdGhpcyBpcyB0aGUgZmlyc3QgdGltZSB3ZSdyZSBkZXBsb3lpbmcgdGhpcyBzdGFjayAob3IgdXBncmFkaW5nIGZyb20gb2xkIHRvIG5ldyBib290c3RyYXApXG4gICAgY29uc3QgY3VycmVudEttc0tleUlkID0gY3VycmVudC5wYXJhbWV0ZXJzLkZpbGVBc3NldHNCdWNrZXRLbXNLZXlJZDtcbiAgICBjb25zdCBrbXNLZXlJZCA9XG4gICAgICBwYXJhbXMua21zS2V5SWQgPz9cbiAgICAgIChwYXJhbXMuY3JlYXRlQ3VzdG9tZXJNYXN0ZXJLZXkgPT09IHRydWVcbiAgICAgICAgPyBDUkVBVEVfTkVXX0tFWVxuICAgICAgICA6IHBhcmFtcy5jcmVhdGVDdXN0b21lck1hc3RlcktleSA9PT0gZmFsc2UgfHwgY3VycmVudEttc0tleUlkID09PSB1bmRlZmluZWRcbiAgICAgICAgICA/IFVTRV9BV1NfTUFOQUdFRF9LRVlcbiAgICAgICAgICA6IHVuZGVmaW5lZCk7XG5cbiAgICAvKiBBIHBlcm1pc3Npb25zIGJvdW5kYXJ5IGNhbiBiZSBwcm92aWRlZCB2aWE6XG4gICAgICogICAgLSB0aGUgZmxhZyBpbmRpY2F0aW5nIHRoZSBleGFtcGxlIG9uZSBzaG91bGQgYmUgdXNlZFxuICAgICAqICAgIC0gdGhlIG5hbWUgaW5kaWNhdGluZyB0aGUgY3VzdG9tIHBlcm1pc3Npb25zIGJvdW5kYXJ5IHRvIGJlIHVzZWRcbiAgICAgKiBSZS1ib290c3RyYXBwaW5nIHdpbGwgTk9UIGJlIGJsb2NrZWQgYnkgZWl0aGVyIHRpZ2h0ZW5pbmcgb3IgcmVsYXhpbmcgdGhlIHBlcm1pc3Npb25zJyBib3VuZGFyeS5cbiAgICAgKi9cblxuICAgIC8vIElucHV0UGVybWlzc2lvbnNCb3VuZGFyeSBpcyBhbiBgYW55YCB0eXBlIGFuZCBpZiBpdCBpcyBub3QgZGVmaW5lZCBpdFxuICAgIC8vIGFwcGVhcnMgYXMgYW4gZW1wdHkgc3RyaW5nICcnLiBXZSBuZWVkIHRvIGZvcmNlIGl0IHRvIGV2YWx1YXRlIGFuIGVtcHR5IHN0cmluZ1xuICAgIC8vIGFzIHVuZGVmaW5lZFxuICAgIGNvbnN0IGN1cnJlbnRQZXJtaXNzaW9uc0JvdW5kYXJ5OiBzdHJpbmcgfCB1bmRlZmluZWQgPSBjdXJyZW50LnBhcmFtZXRlcnMuSW5wdXRQZXJtaXNzaW9uc0JvdW5kYXJ5IHx8IHVuZGVmaW5lZDtcbiAgICBjb25zdCBpbnB1dFBvbGljeU5hbWUgPSBwYXJhbXMuZXhhbXBsZVBlcm1pc3Npb25zQm91bmRhcnlcbiAgICAgID8gQ0RLX0JPT1RTVFJBUF9QRVJNSVNTSU9OU19CT1VOREFSWVxuICAgICAgOiBwYXJhbXMuY3VzdG9tUGVybWlzc2lvbnNCb3VuZGFyeTtcbiAgICBsZXQgcG9saWN5TmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIGlmIChpbnB1dFBvbGljeU5hbWUpIHtcbiAgICAgIC8vIElmIHRoZSBleGFtcGxlIHBvbGljeSBpcyBub3QgYWxyZWFkeSBpbiBwbGFjZSwgaXQgbXVzdCBiZSBjcmVhdGVkLlxuICAgICAgY29uc3Qgc2RrID0gKGF3YWl0IHNka1Byb3ZpZGVyLmZvckVudmlyb25tZW50KGVudmlyb25tZW50LCBNb2RlLkZvcldyaXRpbmcpKS5zZGs7XG4gICAgICBwb2xpY3lOYW1lID0gYXdhaXQgdGhpcy5nZXRQb2xpY3lOYW1lKGVudmlyb25tZW50LCBzZGssIGlucHV0UG9saWN5TmFtZSwgcGFydGl0aW9uLCBwYXJhbXMpO1xuICAgIH1cbiAgICBpZiAoY3VycmVudFBlcm1pc3Npb25zQm91bmRhcnkgIT09IHBvbGljeU5hbWUpIHtcbiAgICAgIGlmICghY3VycmVudFBlcm1pc3Npb25zQm91bmRhcnkpIHtcbiAgICAgICAgd2FybmluZyhgQWRkaW5nIG5ldyBwZXJtaXNzaW9ucyBib3VuZGFyeSAke3BvbGljeU5hbWV9YCk7XG4gICAgICB9IGVsc2UgaWYgKCFwb2xpY3lOYW1lKSB7XG4gICAgICAgIHdhcm5pbmcoYFJlbW92aW5nIGV4aXN0aW5nIHBlcm1pc3Npb25zIGJvdW5kYXJ5ICR7Y3VycmVudFBlcm1pc3Npb25zQm91bmRhcnl9YCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB3YXJuaW5nKGBDaGFuZ2luZyBwZXJtaXNzaW9ucyBib3VuZGFyeSBmcm9tICR7Y3VycmVudFBlcm1pc3Npb25zQm91bmRhcnl9IHRvICR7cG9saWN5TmFtZX1gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gY3VycmVudC51cGRhdGUoXG4gICAgICBib290c3RyYXBUZW1wbGF0ZSxcbiAgICAgIHtcbiAgICAgICAgRmlsZUFzc2V0c0J1Y2tldE5hbWU6IHBhcmFtcy5idWNrZXROYW1lLFxuICAgICAgICBGaWxlQXNzZXRzQnVja2V0S21zS2V5SWQ6IGttc0tleUlkLFxuICAgICAgICAvLyBFbXB0eSBhcnJheSBiZWNvbWVzIGVtcHR5IHN0cmluZ1xuICAgICAgICBUcnVzdGVkQWNjb3VudHM6IHRydXN0ZWRBY2NvdW50cy5qb2luKCcsJyksXG4gICAgICAgIFRydXN0ZWRBY2NvdW50c0Zvckxvb2t1cDogdHJ1c3RlZEFjY291bnRzRm9yTG9va3VwLmpvaW4oJywnKSxcbiAgICAgICAgQ2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llczogY2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llcy5qb2luKCcsJyksXG4gICAgICAgIFF1YWxpZmllcjogcGFyYW1zLnF1YWxpZmllcixcbiAgICAgICAgUHVibGljQWNjZXNzQmxvY2tDb25maWd1cmF0aW9uOlxuICAgICAgICAgIHBhcmFtcy5wdWJsaWNBY2Nlc3NCbG9ja0NvbmZpZ3VyYXRpb24gfHwgcGFyYW1zLnB1YmxpY0FjY2Vzc0Jsb2NrQ29uZmlndXJhdGlvbiA9PT0gdW5kZWZpbmVkXG4gICAgICAgICAgICA/ICd0cnVlJ1xuICAgICAgICAgICAgOiAnZmFsc2UnLFxuICAgICAgICBJbnB1dFBlcm1pc3Npb25zQm91bmRhcnk6IHBvbGljeU5hbWUsXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICB0ZXJtaW5hdGlvblByb3RlY3Rpb246IG9wdGlvbnMudGVybWluYXRpb25Qcm90ZWN0aW9uID8/IGN1cnJlbnQudGVybWluYXRpb25Qcm90ZWN0aW9uLFxuICAgICAgfSxcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXRQb2xpY3lOYW1lKFxuICAgIGVudmlyb25tZW50OiBjeGFwaS5FbnZpcm9ubWVudCxcbiAgICBzZGs6IFNESyxcbiAgICBwZXJtaXNzaW9uc0JvdW5kYXJ5OiBzdHJpbmcsXG4gICAgcGFydGl0aW9uOiBzdHJpbmcsXG4gICAgcGFyYW1zOiBCb290c3RyYXBwaW5nUGFyYW1ldGVycyxcbiAgKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBpZiAocGVybWlzc2lvbnNCb3VuZGFyeSAhPT0gQ0RLX0JPT1RTVFJBUF9QRVJNSVNTSU9OU19CT1VOREFSWSkge1xuICAgICAgdGhpcy52YWxpZGF0ZVBvbGljeU5hbWUocGVybWlzc2lvbnNCb3VuZGFyeSk7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHBlcm1pc3Npb25zQm91bmRhcnkpO1xuICAgIH1cbiAgICAvLyBpZiBubyBRdWFsaWZpZXIgaXMgc3VwcGxpZWQsIHJlc29ydCB0byB0aGUgZGVmYXVsdCBvbmVcbiAgICBjb25zdCBhcm4gPSBhd2FpdCB0aGlzLmdldEV4YW1wbGVQZXJtaXNzaW9uc0JvdW5kYXJ5KFxuICAgICAgcGFyYW1zLnF1YWxpZmllciA/PyAnaG5iNjU5ZmRzJyxcbiAgICAgIHBhcnRpdGlvbixcbiAgICAgIGVudmlyb25tZW50LmFjY291bnQsXG4gICAgICBzZGssXG4gICAgKTtcbiAgICBjb25zdCBwb2xpY3lOYW1lID0gYXJuLnNwbGl0KCcvJykucG9wKCk7XG4gICAgaWYgKCFwb2xpY3lOYW1lKSB7XG4gICAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKCdDb3VsZCBub3QgcmV0cmlldmUgdGhlIGV4YW1wbGUgcGVybWlzc2lvbiBib3VuZGFyeSEnKTtcbiAgICB9XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShwb2xpY3lOYW1lKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZ2V0RXhhbXBsZVBlcm1pc3Npb25zQm91bmRhcnkoXG4gICAgcXVhbGlmaWVyOiBzdHJpbmcsXG4gICAgcGFydGl0aW9uOiBzdHJpbmcsXG4gICAgYWNjb3VudDogc3RyaW5nLFxuICAgIHNkazogU0RLLFxuICApOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGNvbnN0IGlhbSA9IHNkay5pYW0oKTtcblxuICAgIGxldCBwb2xpY3lOYW1lID0gYGNkay0ke3F1YWxpZmllcn0tcGVybWlzc2lvbnMtYm91bmRhcnlgO1xuICAgIGNvbnN0IGFybiA9IGBhcm46JHtwYXJ0aXRpb259OmlhbTo6JHthY2NvdW50fTpwb2xpY3kvJHtwb2xpY3lOYW1lfWA7XG5cbiAgICB0cnkge1xuICAgICAgbGV0IGdldFBvbGljeVJlc3AgPSBhd2FpdCBpYW0uZ2V0UG9saWN5KHsgUG9saWN5QXJuOiBhcm4gfSk7XG4gICAgICBpZiAoZ2V0UG9saWN5UmVzcC5Qb2xpY3kpIHtcbiAgICAgICAgcmV0dXJuIGFybjtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlOiBhbnkpIHtcbiAgICAgIC8vIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9JQU0vbGF0ZXN0L0FQSVJlZmVyZW5jZS9BUElfR2V0UG9saWN5Lmh0bWwjQVBJX0dldFBvbGljeV9FcnJvcnNcbiAgICAgIGlmIChlLm5hbWUgPT09ICdOb1N1Y2hFbnRpdHknKSB7XG4gICAgICAgIC8vbm9vcCwgcHJvY2VlZCB3aXRoIGNyZWF0aW5nIHRoZSBwb2xpY3lcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IGU7XG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgcG9saWN5RG9jID0ge1xuICAgICAgVmVyc2lvbjogJzIwMTItMTAtMTcnLFxuICAgICAgU3RhdGVtZW50OiBbXG4gICAgICAgIHtcbiAgICAgICAgICBBY3Rpb246IFsnKiddLFxuICAgICAgICAgIFJlc291cmNlOiAnKicsXG4gICAgICAgICAgRWZmZWN0OiAnQWxsb3cnLFxuICAgICAgICAgIFNpZDogJ0V4cGxpY2l0QWxsb3dBbGwnLFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgQ29uZGl0aW9uOiB7XG4gICAgICAgICAgICBTdHJpbmdFcXVhbHM6IHtcbiAgICAgICAgICAgICAgJ2lhbTpQZXJtaXNzaW9uc0JvdW5kYXJ5JzogYGFybjoke3BhcnRpdGlvbn06aWFtOjoke2FjY291bnR9OnBvbGljeS9jZGstJHtxdWFsaWZpZXJ9LXBlcm1pc3Npb25zLWJvdW5kYXJ5YCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgICBBY3Rpb246IFtcbiAgICAgICAgICAgICdpYW06Q3JlYXRlVXNlcicsXG4gICAgICAgICAgICAnaWFtOkNyZWF0ZVJvbGUnLFxuICAgICAgICAgICAgJ2lhbTpQdXRSb2xlUGVybWlzc2lvbnNCb3VuZGFyeScsXG4gICAgICAgICAgICAnaWFtOlB1dFVzZXJQZXJtaXNzaW9uc0JvdW5kYXJ5JyxcbiAgICAgICAgICBdLFxuICAgICAgICAgIFJlc291cmNlOiAnKicsXG4gICAgICAgICAgRWZmZWN0OiAnQWxsb3cnLFxuICAgICAgICAgIFNpZDogJ0RlbnlBY2Nlc3NJZlJlcXVpcmVkUGVybUJvdW5kYXJ5SXNOb3RCZWluZ0FwcGxpZWQnLFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgQWN0aW9uOiBbXG4gICAgICAgICAgICAnaWFtOkNyZWF0ZVBvbGljeVZlcnNpb24nLFxuICAgICAgICAgICAgJ2lhbTpEZWxldGVQb2xpY3knLFxuICAgICAgICAgICAgJ2lhbTpEZWxldGVQb2xpY3lWZXJzaW9uJyxcbiAgICAgICAgICAgICdpYW06U2V0RGVmYXVsdFBvbGljeVZlcnNpb24nLFxuICAgICAgICAgIF0sXG4gICAgICAgICAgUmVzb3VyY2U6IGBhcm46JHtwYXJ0aXRpb259OmlhbTo6JHthY2NvdW50fTpwb2xpY3kvY2RrLSR7cXVhbGlmaWVyfS1wZXJtaXNzaW9ucy1ib3VuZGFyeWAsXG4gICAgICAgICAgRWZmZWN0OiAnRGVueScsXG4gICAgICAgICAgU2lkOiAnRGVueVBlcm1Cb3VuZGFyeUlBTVBvbGljeUFsdGVyYXRpb24nLFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgQWN0aW9uOiBbJ2lhbTpEZWxldGVVc2VyUGVybWlzc2lvbnNCb3VuZGFyeScsICdpYW06RGVsZXRlUm9sZVBlcm1pc3Npb25zQm91bmRhcnknXSxcbiAgICAgICAgICBSZXNvdXJjZTogJyonLFxuICAgICAgICAgIEVmZmVjdDogJ0RlbnknLFxuICAgICAgICAgIFNpZDogJ0RlbnlSZW1vdmFsT2ZQZXJtQm91bmRhcnlGcm9tQW55VXNlck9yUm9sZScsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH07XG4gICAgY29uc3QgcmVxdWVzdCA9IHtcbiAgICAgIFBvbGljeU5hbWU6IHBvbGljeU5hbWUsXG4gICAgICBQb2xpY3lEb2N1bWVudDogSlNPTi5zdHJpbmdpZnkocG9saWN5RG9jKSxcbiAgICB9O1xuICAgIGNvbnN0IGNyZWF0ZVBvbGljeVJlc3BvbnNlID0gYXdhaXQgaWFtLmNyZWF0ZVBvbGljeShyZXF1ZXN0KTtcbiAgICBpZiAoY3JlYXRlUG9saWN5UmVzcG9uc2UuUG9saWN5Py5Bcm4pIHtcbiAgICAgIHJldHVybiBjcmVhdGVQb2xpY3lSZXNwb25zZS5Qb2xpY3kuQXJuO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgVG9vbGtpdEVycm9yKGBDb3VsZCBub3QgcmV0cmlldmUgdGhlIGV4YW1wbGUgcGVybWlzc2lvbiBib3VuZGFyeSAke2Fybn0hYCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB2YWxpZGF0ZVBvbGljeU5hbWUocGVybWlzc2lvbnNCb3VuZGFyeTogc3RyaW5nKSB7XG4gICAgLy8gaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL0lBTS9sYXRlc3QvQVBJUmVmZXJlbmNlL0FQSV9DcmVhdGVQb2xpY3kuaHRtbFxuICAgIC8vIEFkZGVkIHN1cHBvcnQgZm9yIHBvbGljeSBuYW1lcyB3aXRoIGEgcGF0aFxuICAgIC8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vYXdzL2F3cy1jZGsvaXNzdWVzLzI2MzIwXG4gICAgY29uc3QgcmVnZXhwOiBSZWdFeHAgPSAvW1xcdytcXC89LC5ALV0rLztcbiAgICBjb25zdCBtYXRjaGVzID0gcmVnZXhwLmV4ZWMocGVybWlzc2lvbnNCb3VuZGFyeSk7XG4gICAgaWYgKCEobWF0Y2hlcyAmJiBtYXRjaGVzLmxlbmd0aCA9PT0gMSAmJiBtYXRjaGVzWzBdID09PSBwZXJtaXNzaW9uc0JvdW5kYXJ5KSkge1xuICAgICAgdGhyb3cgbmV3IFRvb2xraXRFcnJvcihgVGhlIHBlcm1pc3Npb25zIGJvdW5kYXJ5IG5hbWUgJHtwZXJtaXNzaW9uc0JvdW5kYXJ5fSBkb2VzIG5vdCBtYXRjaCB0aGUgSUFNIGNvbnZlbnRpb25zLmApO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgY3VzdG9tQm9vdHN0cmFwKFxuICAgIGVudmlyb25tZW50OiBjeGFwaS5FbnZpcm9ubWVudCxcbiAgICBzZGtQcm92aWRlcjogU2RrUHJvdmlkZXIsXG4gICAgb3B0aW9uczogQm9vdHN0cmFwRW52aXJvbm1lbnRPcHRpb25zID0ge30sXG4gICk6IFByb21pc2U8U3VjY2Vzc2Z1bERlcGxveVN0YWNrUmVzdWx0PiB7XG4gICAgLy8gTG9vayBhdCB0aGUgdGVtcGxhdGUsIGRlY2lkZSB3aGV0aGVyIGl0J3MgbW9zdCBsaWtlbHkgYSBsZWdhY3kgb3IgbW9kZXJuIGJvb3RzdHJhcFxuICAgIC8vIHRlbXBsYXRlLCBhbmQgdXNlIHRoZSByaWdodCBib290c3RyYXBwZXIgZm9yIHRoYXQuXG4gICAgY29uc3QgdmVyc2lvbiA9IGJvb3RzdHJhcFZlcnNpb25Gcm9tVGVtcGxhdGUoYXdhaXQgdGhpcy5sb2FkVGVtcGxhdGUoKSk7XG4gICAgaWYgKHZlcnNpb24gPT09IDApIHtcbiAgICAgIHJldHVybiB0aGlzLmxlZ2FjeUJvb3RzdHJhcChlbnZpcm9ubWVudCwgc2RrUHJvdmlkZXIsIG9wdGlvbnMpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdGhpcy5tb2Rlcm5Cb290c3RyYXAoZW52aXJvbm1lbnQsIHNka1Byb3ZpZGVyLCBvcHRpb25zKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGxvYWRUZW1wbGF0ZShwYXJhbXM6IEJvb3RzdHJhcHBpbmdQYXJhbWV0ZXJzID0ge30pOiBQcm9taXNlPGFueT4ge1xuICAgIHN3aXRjaCAodGhpcy5zb3VyY2Uuc291cmNlKSB7XG4gICAgICBjYXNlICdjdXN0b20nOlxuICAgICAgICByZXR1cm4gbG9hZFN0cnVjdHVyZWRGaWxlKHRoaXMuc291cmNlLnRlbXBsYXRlRmlsZSk7XG4gICAgICBjYXNlICdkZWZhdWx0JzpcbiAgICAgICAgcmV0dXJuIGxvYWRTdHJ1Y3R1cmVkRmlsZShwYXRoLmpvaW4ocm9vdERpcigpLCAnbGliJywgJ2FwaScsICdib290c3RyYXAnLCAnYm9vdHN0cmFwLXRlbXBsYXRlLnlhbWwnKSk7XG4gICAgICBjYXNlICdsZWdhY3knOlxuICAgICAgICByZXR1cm4gbGVnYWN5Qm9vdHN0cmFwVGVtcGxhdGUocGFyYW1zKTtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBNYWdpYyBwYXJhbWV0ZXIgdmFsdWUgdGhhdCB3aWxsIGNhdXNlIHRoZSBib290c3RyYXAtdGVtcGxhdGUueW1sIHRvIE5PVCBjcmVhdGUgYSBDTUsgYnV0IHVzZSB0aGUgZGVmYXVsdCBrZXlcbiAqL1xuY29uc3QgVVNFX0FXU19NQU5BR0VEX0tFWSA9ICdBV1NfTUFOQUdFRF9LRVknO1xuXG4vKipcbiAqIE1hZ2ljIHBhcmFtZXRlciB2YWx1ZSB0aGF0IHdpbGwgY2F1c2UgdGhlIGJvb3RzdHJhcC10ZW1wbGF0ZS55bWwgdG8gY3JlYXRlIGEgQ01LXG4gKi9cbmNvbnN0IENSRUFURV9ORVdfS0VZID0gJyc7XG4vKipcbiAqIFBhcmFtZXRlciB2YWx1ZSBpbmRpY2F0aW5nIHRoZSB1c2Ugb2YgdGhlIGRlZmF1bHQsIENESyBwcm92aWRlZCBwZXJtaXNzaW9ucyBib3VuZGFyeSBmb3IgYm9vdHN0cmFwLXRlbXBsYXRlLnltbFxuICovXG5jb25zdCBDREtfQk9PVFNUUkFQX1BFUk1JU1NJT05TX0JPVU5EQVJZID0gJ0NES19CT09UU1RSQVBfUEVSTUlTU0lPTlNfQk9VTkRBUlknO1xuXG4vKipcbiAqIFNwbGl0IGFuIGFycmF5LWxpa2UgQ2xvdWRGb3JtYXRpb24gcGFyYW1ldGVyIG9uICxcbiAqXG4gKiBBbiBlbXB0eSBzdHJpbmcgaXMgdGhlIGVtcHR5IGFycmF5IC