UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

320 lines 40.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AwsCliCompatible = void 0; const child_process = require("child_process"); const os = require("os"); const path = require("path"); const util = require("util"); const AWS = require("aws-sdk"); const fs = require("fs-extra"); const promptly = require("promptly"); const _env_1 = require("./_env"); const aws_sdk_inifile_1 = require("./aws-sdk-inifile"); const sdk_ini_file_1 = require("./sdk_ini_file"); /** * Behaviors to match AWS CLI * * See these links: * * https://docs.aws.amazon.com/cli/latest/topic/config-vars.html * https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html */ class AwsCliCompatible { /** * Build an AWS CLI-compatible credential chain provider * * This is similar to the default credential provider chain created by the SDK * except: * * 1. Accepts profile argument in the constructor (the SDK must have it prepopulated * in the environment). * 2. Conditionally checks EC2 credentials, because checking for EC2 * credentials on a non-EC2 machine may lead to long delays (in the best case) * or an exception (in the worst case). * 3. Respects $AWS_SHARED_CREDENTIALS_FILE. * 4. Respects $AWS_DEFAULT_PROFILE in addition to $AWS_PROFILE. */ static async credentialChain(options = {}) { // Force reading the `config` file if it exists by setting the appropriate // environment variable. await forceSdkToReadConfigIfPresent(); // To match AWS CLI behavior, if a profile is explicitly given using --profile, // we use that to the exclusion of everything else (note: this does not apply // to AWS_PROFILE, environment credentials still take precedence over AWS_PROFILE) if (options.profile) { return new AWS.CredentialProviderChain(iniFileCredentialFactories(options.profile, options.httpOptions)); } const implicitProfile = process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || 'default'; const sources = [ () => new AWS.EnvironmentCredentials('AWS'), () => new AWS.EnvironmentCredentials('AMAZON'), ...iniFileCredentialFactories(implicitProfile, options.httpOptions), ]; if (options.containerCreds ?? hasEcsCredentials()) { sources.push(() => new AWS.ECSCredentials()); } else if (hasWebIdentityCredentials()) { // else if: we have found WebIdentityCredentials as provided by EKS ServiceAccounts sources.push(() => new AWS.TokenFileWebIdentityCredentials()); } else if (options.ec2instance ?? await isEc2Instance()) { // else if: don't get EC2 creds if we should have gotten ECS or EKS creds // ECS and EKS instances also run on EC2 boxes but the creds represent something different. // Same behavior as upstream code. sources.push(() => new AWS.EC2MetadataCredentials()); } return new AWS.CredentialProviderChain(sources); function profileCredentials(profileName) { return new aws_sdk_inifile_1.PatchedSharedIniFileCredentials({ profile: profileName, filename: credentialsFileName(), httpOptions: options.httpOptions, tokenCodeFn, }); } function iniFileCredentialFactories(theProfile, theHttpOptions) { return [ () => profileCredentials(theProfile), () => new AWS.SsoCredentials({ profile: theProfile, httpOptions: theHttpOptions, }), () => new AWS.ProcessCredentials({ profile: theProfile }), ]; } } /** * Return the default region in a CLI-compatible way * * Mostly copied from node_loader.js, but with the following differences to make it * AWS CLI compatible: * * 1. Takes a profile name as an argument (instead of forcing it to be taken from $AWS_PROFILE). * This requires having made a copy of the SDK's `SharedIniFile` (the original * does not take an argument). * 2. $AWS_DEFAULT_PROFILE and $AWS_DEFAULT_REGION are also respected. * * Lambda and CodeBuild set the $AWS_REGION variable. */ static async region(options = {}) { const profile = options.profile || process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || 'default'; // Defaults inside constructor const toCheck = [ { filename: credentialsFileName(), profile }, { isConfig: true, filename: configFileName(), profile }, { isConfig: true, filename: configFileName(), profile: 'default' }, ]; let region = process.env.AWS_REGION || process.env.AMAZON_REGION || process.env.AWS_DEFAULT_REGION || process.env.AMAZON_DEFAULT_REGION; while (!region && toCheck.length > 0) { const opts = toCheck.shift(); if (await fs.pathExists(opts.filename)) { const configFile = new sdk_ini_file_1.SharedIniFile(opts); const section = await configFile.getProfile(opts.profile); region = section?.region; } } if (!region && (options.ec2instance ?? await isEc2Instance())) { (0, _env_1.debug)('Looking up AWS region in the EC2 Instance Metadata Service (IMDS).'); const imdsOptions = { httpOptions: { timeout: 1000, connectTimeout: 1000 }, maxRetries: 2, }; const metadataService = new AWS.MetadataService(imdsOptions); let token; try { token = await getImdsV2Token(metadataService); } catch (e) { (0, _env_1.debug)(`No IMDSv2 token: ${e}`); } try { region = await getRegionFromImds(metadataService, token); (0, _env_1.debug)(`AWS region from IMDS: ${region}`); } catch (e) { (0, _env_1.debug)(`Unable to retrieve AWS region from IMDS: ${e}`); } } if (!region) { const usedProfile = !profile ? '' : ` (profile: "${profile}")`; region = 'us-east-1'; // This is what the AWS CLI does (0, _env_1.debug)(`Unable to determine AWS region from environment or AWS configuration${usedProfile}, defaulting to '${region}'`); } return region; } } exports.AwsCliCompatible = AwsCliCompatible; /** * Return whether it looks like we'll have ECS credentials available */ function hasEcsCredentials() { return AWS.ECSCredentials.prototype.isConfiguredForEcsCredentials(); } /** * Return whether it looks like we'll have WebIdentityCredentials (that's what EKS uses) available * No check like hasEcsCredentials available, so have to implement our own. * @see https://github.com/aws/aws-sdk-js/blob/3ccfd94da07234ae87037f55c138392f38b6881d/lib/credentials/token_file_web_identity_credentials.js#L59 */ function hasWebIdentityCredentials() { return Boolean(process.env.AWS_ROLE_ARN && process.env.AWS_WEB_IDENTITY_TOKEN_FILE); } /** * Return whether we're on an EC2 instance */ async function isEc2Instance() { if (isEc2InstanceCache === undefined) { (0, _env_1.debug)("Determining if we're on an EC2 instance."); let instance = false; if (process.platform === 'win32') { // https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/identify_ec2_instances.html try { const result = await util.promisify(child_process.exec)('wmic path win32_computersystemproduct get uuid', { encoding: 'utf-8' }); // output looks like // UUID // EC2AE145-D1DC-13B2-94ED-01234ABCDEF const lines = result.stdout.toString().split('\n'); instance = lines.some(x => matchesRegex(/^ec2/i, x)); } catch (e) { // Modern machines may not have wmic.exe installed. No reason to fail, just assume it's not an EC2 instance. (0, _env_1.debug)(`Checking using WMIC failed, assuming NOT an EC2 instance: ${e.message} (pass --ec2creds to force)`); instance = false; } } else { // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/identify_ec2_instances.html const files = [ // This recognizes the Xen hypervisor based instances (pre-5th gen) ['/sys/hypervisor/uuid', /^ec2/i], // This recognizes the new Hypervisor (5th-gen instances and higher) // Can't use the advertised file '/sys/devices/virtual/dmi/id/product_uuid' because it requires root to read. // Instead, sys_vendor contains something like 'Amazon EC2'. ['/sys/devices/virtual/dmi/id/sys_vendor', /ec2/i], ]; for (const [file, re] of files) { if (matchesRegex(re, readIfPossible(file))) { instance = true; break; } } } (0, _env_1.debug)(instance ? 'Looks like an EC2 instance.' : 'Does not look like an EC2 instance.'); isEc2InstanceCache = instance; } return isEc2InstanceCache; } let isEc2InstanceCache = undefined; /** * Attempts to get a Instance Metadata Service V2 token */ async function getImdsV2Token(metadataService) { (0, _env_1.debug)('Attempting to retrieve an IMDSv2 token.'); return new Promise((resolve, reject) => { metadataService.request('/latest/api/token', { method: 'PUT', headers: { 'x-aws-ec2-metadata-token-ttl-seconds': '60' }, }, (err, token) => { if (err) { reject(err); } else if (!token) { reject(new Error('IMDS did not return a token.')); } else { resolve(token); } }); }); } /** * Attempts to get the region from the Instance Metadata Service */ async function getRegionFromImds(metadataService, token) { (0, _env_1.debug)('Retrieving the AWS region from the IMDS.'); let options = {}; if (token) { options = { headers: { 'x-aws-ec2-metadata-token': token } }; } return new Promise((resolve, reject) => { metadataService.request('/latest/dynamic/instance-identity/document', options, (err, instanceIdentityDocument) => { if (err) { reject(err); } else if (!instanceIdentityDocument) { reject(new Error('IMDS did not return an Instance Identity Document.')); } else { try { resolve(JSON.parse(instanceIdentityDocument).region); } catch (e) { reject(e); } } }); }); } function homeDir() { return process.env.HOME || process.env.USERPROFILE || (process.env.HOMEPATH ? ((process.env.HOMEDRIVE || 'C:/') + process.env.HOMEPATH) : null) || os.homedir(); } function credentialsFileName() { return process.env.AWS_SHARED_CREDENTIALS_FILE || path.join(homeDir(), '.aws', 'credentials'); } function configFileName() { return process.env.AWS_CONFIG_FILE || path.join(homeDir(), '.aws', 'config'); } /** * Force the JS SDK to honor the ~/.aws/config file (and various settings therein) * * For example, there is just *NO* way to do AssumeRole credentials as long as AWS_SDK_LOAD_CONFIG is not set, * or read credentials from that file. * * The SDK crashes if the variable is set but the file does not exist, so conditionally set it. */ async function forceSdkToReadConfigIfPresent() { if (await fs.pathExists(configFileName())) { process.env.AWS_SDK_LOAD_CONFIG = '1'; } } function matchesRegex(re, s) { return s !== undefined && re.exec(s) !== null; } /** * Read a file if it exists, or return undefined * * Not async because it is used in the constructor */ function readIfPossible(filename) { try { if (!fs.pathExistsSync(filename)) { return undefined; } return fs.readFileSync(filename, { encoding: 'utf-8' }); } catch (e) { (0, _env_1.debug)(e); return undefined; } } /** * Ask user for MFA token for given serial * * Result is send to callback function for SDK to authorize the request */ async function tokenCodeFn(serialArn, cb) { (0, _env_1.debug)('Require MFA token for serial ARN', serialArn); try { const token = await promptly.prompt(`MFA token for ${serialArn}: `, { trim: true, default: '', }); (0, _env_1.debug)('Successfully got MFA token from user'); cb(undefined, token); } catch (err) { (0, _env_1.debug)('Failed to get MFA token', err); cb(err); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXdzY2xpLWNvbXBhdGlibGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhd3NjbGktY29tcGF0aWJsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwrQ0FBK0M7QUFDL0MseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2QkFBNkI7QUFDN0IsK0JBQStCO0FBQy9CLCtCQUErQjtBQUMvQixxQ0FBcUM7QUFDckMsaUNBQStCO0FBQy9CLHVEQUFvRTtBQUNwRSxpREFBK0M7QUFFL0M7Ozs7Ozs7R0FPRztBQUNILE1BQWEsZ0JBQWdCO0lBQzNCOzs7Ozs7Ozs7Ozs7O09BYUc7SUFDSSxNQUFNLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxVQUFrQyxFQUFFO1FBQ3RFLDBFQUEwRTtRQUMxRSx3QkFBd0I7UUFDeEIsTUFBTSw2QkFBNkIsRUFBRSxDQUFDO1FBRXRDLCtFQUErRTtRQUMvRSw2RUFBNkU7UUFDN0Usa0ZBQWtGO1FBQ2xGLElBQUksT0FBTyxDQUFDLE9BQU8sRUFBRTtZQUNuQixPQUFPLElBQUksR0FBRyxDQUFDLHVCQUF1QixDQUFDLDBCQUEwQixDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7U0FDMUc7UUFFRCxNQUFNLGVBQWUsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixJQUFJLFNBQVMsQ0FBQztRQUVoRyxNQUFNLE9BQU8sR0FBRztZQUNkLEdBQUcsRUFBRSxDQUFDLElBQUksR0FBRyxDQUFDLHNCQUFzQixDQUFDLEtBQUssQ0FBQztZQUMzQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxzQkFBc0IsQ0FBQyxRQUFRLENBQUM7WUFDOUMsR0FBRywwQkFBMEIsQ0FBQyxlQUFlLEVBQUUsT0FBTyxDQUFDLFdBQVcsQ0FBQztTQUNwRSxDQUFDO1FBRUYsSUFBSSxPQUFPLENBQUMsY0FBYyxJ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QUMsRUFBRTtZQUFFLE9BQU8sU0FBUyxDQUFDO1NBQUU7UUFDdkQsT0FBTyxFQUFFLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO0tBQ3pEO0lBQUMsT0FBTyxDQUFNLEVBQUU7UUFDZixJQUFBLFlBQUssRUFBQyxDQUFDLENBQUMsQ0FBQztRQUNULE9BQU8sU0FBUyxDQUFDO0tBQ2xCO0FBQ0gsQ0FBQztBQWNEOzs7O0dBSUc7QUFDSCxLQUFLLFVBQVUsV0FBVyxDQUFDLFNBQWlCLEVBQUUsRUFBeUM7SUFDckYsSUFBQSxZQUFLLEVBQUMsa0NBQWtDLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDckQsSUFBSTtRQUNGLE1BQU0sS0FBSyxHQUFXLE1BQU0sUUFBUSxDQUFDLE1BQU0sQ0FBQyxpQkFBaUIsU0FBUyxJQUFJLEVBQUU7WUFDMUUsSUFBSSxFQUFFLElBQUk7WUFDVixPQUFPLEVBQUUsRUFBRTtTQUNaLENBQUMsQ0FBQztRQUNILElBQUEsWUFBSyxFQUFDLHNDQUFzQyxDQUFDLENBQUM7UUFDOUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsQ0FBQztLQUN0QjtJQUFDLE9BQU8sR0FBUSxFQUFFO1FBQ2pCLElBQUEsWUFBSyxFQUFDLHlCQUF5QixFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ3RDLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQztLQUNUO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNoaWxkX3Byb2Nlc3MgZnJvbSAnY2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgKiBhcyBvcyBmcm9tICdvcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICd1dGlsJztcbmltcG9ydCAqIGFzIEFXUyBmcm9tICdhd3Mtc2RrJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzLWV4dHJhJztcbmltcG9ydCAqIGFzIHByb21wdGx5IGZyb20gJ3Byb21wdGx5JztcbmltcG9ydCB7IGRlYnVnIH0gZnJvbSAnLi9fZW52JztcbmltcG9ydCB7IFBhdGNoZWRTaGFyZWRJbmlGaWxlQ3JlZGVudGlhbHMgfSBmcm9tICcuL2F3cy1zZGstaW5pZmlsZSc7XG5pbXBvcnQgeyBTaGFyZWRJbmlGaWxlIH0gZnJvbSAnLi9zZGtfaW5pX2ZpbGUnO1xuXG4vKipcbiAqIEJlaGF2aW9ycyB0byBtYXRjaCBBV1MgQ0xJXG4gKlxuICogU2VlIHRoZXNlIGxpbmtzOlxuICpcbiAqIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9jbGkvbGF0ZXN0L3RvcGljL2NvbmZpZy12YXJzLmh0bWxcbiAqIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9jbGkvbGF0ZXN0L3VzZXJndWlkZS9jbGktY29uZmlndXJlLWVudnZhcnMuaHRtbFxuICovXG5leHBvcnQgY2xhc3MgQXdzQ2xpQ29tcGF0aWJsZSB7XG4gIC8qKlxuICAgKiBCdWlsZCBhbiBBV1MgQ0xJLWNvbXBhdGlibGUgY3JlZGVudGlhbCBjaGFpbiBwcm92aWRlclxuICAgKlxuICAgKiBUaGlzIGlzIHNpbWlsYXIgdG8gdGhlIGRlZmF1bHQgY3JlZGVudGlhbCBwcm92aWRlciBjaGFpbiBjcmVhdGVkIGJ5IHRoZSBTREtcbiAgICogZXhjZXB0OlxuICAgKlxuICAgKiAxLiBBY2NlcHRzIHByb2ZpbGUgYXJndW1lbnQgaW4gdGhlIGNvbnN0cnVjdG9yICh0aGUgU0RLIG11c3QgaGF2ZSBpdCBwcmVwb3B1bGF0ZWRcbiAgICogICAgaW4gdGhlIGVudmlyb25tZW50KS5cbiAgICogMi4gQ29uZGl0aW9uYWxseSBjaGVja3MgRUMyIGNyZWRlbnRpYWxzLCBiZWNhdXNlIGNoZWNraW5nIGZvciBFQzJcbiAgICogICAgY3JlZGVudGlhbHMgb24gYSBub24tRUMyIG1hY2hpbmUgbWF5IGxlYWQgdG8gbG9uZyBkZWxheXMgKGluIHRoZSBiZXN0IGNhc2UpXG4gICAqICAgIG9yIGFuIGV4Y2VwdGlvbiAoaW4gdGhlIHdvcnN0IGNhc2UpLlxuICAgKiAzLiBSZXNwZWN0cyAkQVdTX1NIQVJFRF9DUkVERU5USUFMU19GSUxFLlxuICAgKiA0LiBSZXNwZWN0cyAkQVdTX0RFRkFVTFRfUFJPRklMRSBpbiBhZGRpdGlvbiB0byAkQVdTX1BST0ZJTEUuXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGFzeW5jIGNyZWRlbnRpYWxDaGFpbihvcHRpb25zOiBDcmVkZW50aWFsQ2hhaW5PcHRpb25zID0ge30pIHtcbiAgICAvLyBGb3JjZSByZWFkaW5nIHRoZSBgY29uZmlnYCBmaWxlIGlmIGl0IGV4aXN0cyBieSBzZXR0aW5nIHRoZSBhcHByb3ByaWF0ZVxuICAgIC8vIGVudmlyb25tZW50IHZhcmlhYmxlLlxuICAgIGF3YWl0IGZvcmNlU2RrVG9SZWFkQ29uZmlnSWZQcmVzZW50KCk7XG5cbiAgICAvLyBUbyBtYXRjaCBBV1MgQ0xJIGJlaGF2aW9yLCBpZiBhIHByb2ZpbGUgaXMgZXhwbGljaXRseSBnaXZlbiB1c2luZyAtLXByb2ZpbGUsXG4gICAgLy8gd2UgdXNlIHRoYXQgdG8gdGhlIGV4Y2x1c2lvbiBvZiBldmVyeXRoaW5nIGVsc2UgKG5vdGU6IHRoaXMgZG9lcyBub3QgYXBwbHlcbiAgICAvLyB0byBBV1NfUFJPRklMRSwgZW52aXJvbm1lbnQgY3JlZGVudGlhbHMgc3RpbGwgdGFrZSBwcmVjZWRlbmNlIG92ZXIgQVdTX1BST0ZJTEUpXG4gICAgaWYgKG9wdGlvbnMucHJvZmlsZSkge1xuICAgICAgcmV0dXJuIG5ldyBBV1MuQ3JlZGVudGlhbFByb3ZpZGVyQ2hhaW4oaW5pRmlsZUNyZWRlbnRpYWxGYWN0b3JpZXMob3B0aW9ucy5wcm9maWxlLCBvcHRpb25zLmh0dHBPcHRpb25zKSk7XG4gICAgfVxuXG4gICAgY29uc3QgaW1wbGljaXRQcm9maWxlID0gcHJvY2Vzcy5lbnYuQVdTX1BST0ZJTEUgfHwgcHJvY2Vzcy5lbnYuQVdTX0RFRkFVTFRfUFJPRklMRSB8fCAnZGVmYXVsdCc7XG5cbiAgICBjb25zdCBzb3VyY2VzID0gW1xuICAgICAgKCkgPT4gbmV3IEFXUy5FbnZpcm9ubWVudENyZWRlbnRpYWxzKCdBV1MnKSxcbiAgICAgICgpID0+IG5ldyBBV1MuRW52aXJvbm1lbnRDcmVkZW50aWFscygnQU1BWk9OJyksXG4gICAgICAuLi5pbmlGaWxlQ3JlZGVudGlhbEZhY3RvcmllcyhpbXBsaWNpdFByb2ZpbGUsIG9wdGlvbnMuaHR0cE9wdGlvbnMpLFxuICAgIF07XG5cbiAgICBpZiAob3B0aW9ucy5jb250YWluZXJDcmVkcyA/PyBoYXNFY3NDcmVkZW50aWFscygpKSB7XG4gICAgICBzb3VyY2VzLnB1c2goKCkgPT4gbmV3IEFXUy5FQ1NDcmVkZW50aWFscygpKTtcbiAgICB9IGVsc2UgaWYgKGhhc1dlYklkZW50aXR5Q3JlZGVudGlhbHMoKSkge1xuICAgICAgLy8gZWxzZSBpZjogd2UgaGF2ZSBmb3VuZCBXZWJJZGVudGl0eUNyZWRlbnRpYWxzIGFzIHByb3ZpZGVkIGJ5IEVLUyBTZXJ2aWNlQWNjb3VudHNcbiAgICAgIHNvdXJjZXMucHVzaCgoKSA9PiBuZXcgQVdTLlRva2VuRmlsZVdlYklkZW50aXR5Q3JlZGVudGlhbHMoKSk7XG4gICAgfSBlbHNlIGlmIChvcHRpb25zLmVjMmluc3RhbmNlID8/IGF3YWl0IGlzRWMySW5zdGFuY2UoKSkge1xuICAgICAgLy8gZWxzZSBpZjogZG9uJ3QgZ2V0IEVDMiBjcmVkcyBpZiB3ZSBzaG91bGQgaGF2ZSBnb3R0ZW4gRUNTIG9yIEVLUyBjcmVkc1xuICAgICAgLy8gRUNTIGFuZCBFS1MgaW5zdGFuY2VzIGFsc28gcnVuIG9uIEVDMiBib3hlcyBidXQgdGhlIGNyZWRzIHJlcHJlc2VudCBzb21ldGhpbmcgZGlmZmVyZW50LlxuICAgICAgLy8gU2FtZSBiZWhhdmlvciBhcyB1cHN0cmVhbSBjb2RlLlxuICAgICAgc291cmNlcy5wdXNoKCgpID0+IG5ldyBBV1MuRUMyTWV0YWRhdGFDcmVkZW50aWFscygpKTtcbiAgICB9XG5cbiAgICByZXR1cm4gbmV3IEFXUy5DcmVkZW50aWFsUHJvdmlkZXJDaGFpbihzb3VyY2VzKTtcblxuICAgIGZ1bmN0aW9uIHByb2ZpbGVDcmVkZW50aWFscyhwcm9maWxlTmFtZTogc3RyaW5nKSB7XG4gICAgICByZXR1cm4gbmV3IFBhdGNoZWRTaGFyZWRJbmlGaWxlQ3JlZGVudGlhbHMoe1xuICAgICAgICBwcm9maWxlOiBwcm9maWxlTmFtZSxcbiAgICAgICAgZmlsZW5hbWU6IGNyZWRlbnRpYWxzRmlsZU5hbWUoKSxcbiAgICAgICAgaHR0cE9wdGlvbnM6IG9wdGlvbnMuaHR0cE9wdGlvbnMsXG4gICAgICAgIHRva2VuQ29kZUZuLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgZnVuY3Rpb24gaW5pRmlsZUNyZWRlbnRpYWxGYWN0b3JpZXModGhlUHJvZmlsZTogc3RyaW5nLCB0aGVIdHRwT3B0aW9ucz86IEFXUy5IVFRQT3B0aW9ucykge1xuICAgICAgcmV0dXJuIFtcbiAgICAgICAgKCkgPT4gcHJvZmlsZUNyZWRlbnRpYWxzKHRoZVByb2ZpbGUpLFxuICAgICAgICAoKSA9PiBuZXcgQVdTLlNzb0NyZWRlbnRpYWxzKHtcbiAgICAgICAgICBwcm9maWxlOiB0aGVQcm9maWxlLFxuICAgICAgICAgIGh0dHBPcHRpb25zOiB0aGVIdHRwT3B0aW9ucyxcbiAgICAgICAgfSksXG4gICAgICAgICgpID0+IG5ldyBBV1MuUHJvY2Vzc0NyZWRlbnRpYWxzKHsgcHJvZmlsZTogdGhlUHJvZmlsZSB9KSxcbiAgICAgIF07XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgZGVmYXVsdCByZWdpb24gaW4gYSBDTEktY29tcGF0aWJsZSB3YXlcbiAgICpcbiAgICogTW9zdGx5IGNvcGllZCBmcm9tIG5vZGVfbG9hZGVyLmpzLCBidXQgd2l0aCB0aGUgZm9sbG93aW5nIGRpZmZlcmVuY2VzIHRvIG1ha2UgaXRcbiAgICogQVdTIENMSSBjb21wYXRpYmxlOlxuICAgKlxuICAgKiAxLiBUYWtlcyBhIHByb2ZpbGUgbmFtZSBhcyBhbiBhcmd1bWVudCAoaW5zdGVhZCBvZiBmb3JjaW5nIGl0IHRvIGJlIHRha2VuIGZyb20gJEFXU19QUk9GSUxFKS5cbiAgICogICAgVGhpcyByZXF1aXJlcyBoYXZpbmcgbWFkZSBhIGNvcHkgb2YgdGhlIFNESydzIGBTaGFyZWRJbmlGaWxlYCAodGhlIG9yaWdpbmFsXG4gICAqICAgIGRvZXMgbm90IHRha2UgYW4gYXJndW1lbnQpLlxuICAgKiAyLiAkQVdTX0RFRkFVTFRfUFJPRklMRSBhbmQgJEFXU19ERUZBVUxUX1JFR0lPTiBhcmUgYWxzbyByZXNwZWN0ZWQuXG4gICAqXG4gICAqIExhbWJkYSBhbmQgQ29kZUJ1aWxkIHNldCB0aGUgJEFXU19SRUdJT04gdmFyaWFibGUuXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGFzeW5jIHJlZ2lvbihvcHRpb25zOiBSZWdpb25PcHRpb25zID0ge30pOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGNvbnN0IHByb2ZpbGUgPSBvcHRpb25zLnByb2ZpbGUgfHwgcHJvY2Vzcy5lbnYuQVdTX1BST0ZJTEUgfHwgcHJvY2Vzcy5lbnYuQVdTX0RFRkFVTFRfUFJPRklMRSB8fCAnZGVmYXVsdCc7XG5cbiAgICAvLyBEZWZhdWx0cyBpbnNpZGUgY29uc3RydWN0b3JcbiAgICBjb25zdCB0b0NoZWNrID0gW1xuICAgICAgeyBmaWxlbmFtZTogY3JlZGVudGlhbHNGaWxlTmFtZSgpLCBwcm9maWxlIH0sXG4gICAgICB7IGlzQ29uZmlnOiB0cnVlLCBmaWxlbmFtZTogY29uZmlnRmlsZU5hbWUoKSwgcHJvZmlsZSB9LFxuICAgICAgeyBpc0NvbmZpZzogdHJ1ZSwgZmlsZW5hbWU6IGNvbmZpZ0ZpbGVOYW1lKCksIHByb2ZpbGU6ICdkZWZhdWx0JyB9LFxuICAgIF07XG5cbiAgICBsZXQgcmVnaW9uID0gcHJvY2Vzcy5lbnYuQVdTX1JFR0lPTiB8fCBwcm9jZXNzLmVudi5BTUFaT05fUkVHSU9OIHx8XG4gICAgICBwcm9jZXNzLmVudi5BV1NfREVGQVVMVF9SRUdJT04gfHwgcHJvY2Vzcy5lbnYuQU1BWk9OX0RFRkFVTFRfUkVHSU9OO1xuXG4gICAgd2hpbGUgKCFyZWdpb24gJiYgdG9DaGVjay5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBvcHRzID0gdG9DaGVjay5zaGlmdCgpITtcbiAgICAgIGlmIChhd2FpdCBmcy5wYXRoRXhpc3RzKG9wdHMuZmlsZW5hbWUpKSB7XG4gICAgICAgIGNvbnN0IGNvbmZpZ0ZpbGUgPSBuZXcgU2hhcmVkSW5pRmlsZShvcHRzKTtcbiAgICAgICAgY29uc3Qgc2VjdGlvbiA9IGF3YWl0IGNvbmZpZ0ZpbGUuZ2V0UHJvZmlsZShvcHRzLnByb2ZpbGUpO1xuICAgICAgICByZWdpb24gPSBzZWN0aW9uPy5yZWdpb247XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFyZWdpb24gJiYgKG9wdGlvbnMuZWMyaW5zdGFuY2UgPz8gYXdhaXQgaXNFYzJJbnN0YW5jZSgpKSkge1xuICAgICAgZGVidWcoJ0xvb2tpbmcgdXAgQVdTIHJlZ2lvbiBpbiB0aGUgRUMyIEluc3RhbmNlIE1ldGFkYXRhIFNlcnZpY2UgKElNRFMpLicpO1xuICAgICAgY29uc3QgaW1kc09wdGlvbnMgPSB7XG4gICAgICAgIGh0dHBPcHRpb25zOiB7IHRpbWVvdXQ6IDEwMDAsIGNvbm5lY3RUaW1lb3V0OiAxMDAwIH0sIG1heFJldHJpZXM6IDIsXG4gICAgICB9O1xuICAgICAgY29uc3QgbWV0YWRhdGFTZXJ2aWNlID0gbmV3IEFXUy5NZXRhZGF0YVNlcnZpY2UoaW1kc09wdGlvbnMpO1xuXG4gICAgICBsZXQgdG9rZW47XG4gICAgICB0cnkge1xuICAgICAgICB0b2tlbiA9IGF3YWl0IGdldEltZHNWMlRva2VuKG1ldGFkYXRhU2VydmljZSk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGRlYnVnKGBObyBJTURTdjIgdG9rZW46ICR7ZX1gKTtcbiAgICAgIH1cblxuICAgICAgdHJ5IHtcbiAgICAgICAgcmVnaW9uID0gYXdhaXQgZ2V0UmVnaW9uRnJvbUltZHMobWV0YWRhdGFTZXJ2aWNlLCB0b2tlbik7XG4gICAgICAgIGRlYnVnKGBBV1MgcmVnaW9uIGZyb20gSU1EUzogJHtyZWdpb259YCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGRlYnVnKGBVbmFibGUgdG8gcmV0cmlldmUgQVdTIHJlZ2lvbiBmcm9tIElNRFM6ICR7ZX1gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoIXJlZ2lvbikge1xuICAgICAgY29uc3QgdXNlZFByb2ZpbGUgPSAhcHJvZmlsZSA/ICcnIDogYCAocHJvZmlsZTogXCIke3Byb2ZpbGV9XCIpYDtcbiAgICAgIHJlZ2lvbiA9ICd1cy1lYXN0LTEnOyAvLyBUaGlzIGlzIHdoYXQgdGhlIEFXUyBDTEkgZG9lc1xuICAgICAgZGVidWcoYFVuYWJsZSB0byBkZXRlcm1pbmUgQVdTIHJlZ2lvbiBmcm9tIGVudmlyb25tZW50IG9yIEFXUyBjb25maWd1cmF0aW9uJHt1c2VkUHJvZmlsZX0sIGRlZmF1bHRpbmcgdG8gJyR7cmVnaW9ufSdgKTtcbiAgICB9XG5cbiAgICByZXR1cm4gcmVnaW9uO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIHdoZXRoZXIgaXQgbG9va3MgbGlrZSB3ZSdsbCBoYXZlIEVDUyBjcmVkZW50aWFscyBhdmFpbGFibGVcbiAqL1xuZnVuY3Rpb24gaGFzRWNzQ3JlZGVudGlhbHMoKTogYm9vbGVhbiB7XG4gIHJldHVybiAoQVdTLkVDU0NyZWRlbnRpYWxzLnByb3RvdHlwZSBhcyBhbnkpLmlzQ29uZmlndXJlZEZvckVjc0NyZWRlbnRpYWxzKCk7XG59XG5cbi8qKlxuICogUmV0dXJuIHdoZXRoZXIgaXQgbG9va3MgbGlrZSB3ZSdsbCBoYXZlIFdlYklkZW50aXR5Q3JlZGVudGlhbHMgKHRoYXQncyB3aGF0IEVLUyB1c2VzKSBhdmFpbGFibGVcbiAqIE5vIGNoZWNrIGxpa2UgaGFzRWNzQ3JlZGVudGlhbHMgYXZhaWxhYmxlLCBzbyBoYXZlIHRvIGltcGxlbWVudCBvdXIgb3duLlxuICogQHNlZSBodHRwczovL2dpdGh1Yi5jb20vYXdzL2F3cy1zZGstanMvYmxvYi8zY2NmZDk0ZGEwNzIzNGFlODcwMzdmNTVjMTM4MzkyZjM4YjY4ODFkL2xpYi9jcmVkZW50aWFscy90b2tlbl9maWxlX3dlYl9pZGVudGl0eV9jcmVkZW50aWFscy5qcyNMNTlcbiAqL1xuZnVuY3Rpb24gaGFzV2ViSWRlbnRpdHlDcmVkZW50aWFscygpOiBib29sZWFuIHtcbiAgcmV0dXJuIEJvb2xlYW4ocHJvY2Vzcy5lbnYuQVdTX1JPTEVfQVJOICYmIHByb2Nlc3MuZW52LkFXU19XRUJfSURFTlRJVFlfVE9LRU5fRklMRSk7XG59XG5cbi8qKlxuICogUmV0dXJuIHdoZXRoZXIgd2UncmUgb24gYW4gRUMyIGluc3RhbmNlXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGlzRWMySW5zdGFuY2UoKSB7XG4gIGlmIChpc0VjMkluc3RhbmNlQ2FjaGUgPT09IHVuZGVmaW5lZCkge1xuICAgIGRlYnVnKFwiRGV0ZXJtaW5pbmcgaWYgd2UncmUgb24gYW4gRUMyIGluc3RhbmNlLlwiKTtcbiAgICBsZXQgaW5zdGFuY2UgPSBmYWxzZTtcbiAgICBpZiAocHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJykge1xuICAgICAgLy8gaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL0FXU0VDMi9sYXRlc3QvV2luZG93c0d1aWRlL2lkZW50aWZ5X2VjMl9pbnN0YW5jZXMuaHRtbFxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdXRpbC5wcm9taXNpZnkoY2hpbGRfcHJvY2Vzcy5leGVjKSgnd21pYyBwYXRoIHdpbjMyX2NvbXB1dGVyc3lzdGVtcHJvZHVjdCBnZXQgdXVpZCcsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSk7XG4gICAgICAgIC8vIG91dHB1dCBsb29rcyBsaWtlXG4gICAgICAgIC8vICBVVUlEXG4gICAgICAgIC8vICBFQzJBRTE0NS1EMURDLTEzQjItOTRFRC0wMTIzNEFCQ0RFRlxuICAgICAgICBjb25zdCBsaW5lcyA9IHJlc3VsdC5zdGRvdXQudG9TdHJpbmcoKS5zcGxpdCgnXFxuJyk7XG4gICAgICAgIGluc3RhbmNlID0gbGluZXMuc29tZSh4ID0+IG1hdGNoZXNSZWdleCgvXmVjMi9pLCB4KSk7XG4gICAgICB9IGNhdGNoIChlOiBhbnkpIHtcbiAgICAgICAgLy8gTW9kZXJuIG1hY2hpbmVzIG1heSBub3QgaGF2ZSB3bWljLmV4ZSBpbnN0YWxsZWQuIE5vIHJlYXNvbiB0byBmYWlsLCBqdXN0IGFzc3VtZSBpdCdzIG5vdCBhbiBFQzIgaW5zdGFuY2UuXG4gICAgICAgIGRlYnVnKGBDaGVja2luZyB1c2luZyBXTUlDIGZhaWxlZCwgYXNzdW1pbmcgTk9UIGFuIEVDMiBpbnN0YW5jZTogJHtlLm1lc3NhZ2V9IChwYXNzIC0tZWMyY3JlZHMgdG8gZm9yY2UpYCk7XG4gICAgICAgIGluc3RhbmNlID0gZmFsc2U7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9BV1NFQzIvbGF0ZXN0L1VzZXJHdWlkZS9pZGVudGlmeV9lYzJfaW5zdGFuY2VzLmh0bWxcbiAgICAgIGNvbnN0IGZpbGVzOiBBcnJheTxbc3RyaW5nLCBSZWdFeHBdPiA9IFtcbiAgICAgICAgLy8gVGhpcyByZWNvZ25pemVzIHRoZSBYZW4gaHlwZXJ2aXNvciBiYXNlZCBpbnN0YW5jZXMgKHByZS01dGggZ2VuKVxuICAgICAgICBbJy9zeXMvaHlwZXJ2aXNvci91dWlkJywgL15lYzIvaV0sXG5cbiAgICAgICAgLy8gVGhpcyByZWNvZ25pemVzIHRoZSBuZXcgSHlwZXJ2aXNvciAoNXRoLWdlbiBpbnN0YW5jZXMgYW5kIGhpZ2hlcilcbiAgICAgICAgLy8gQ2FuJ3QgdXNlIHRoZSBhZHZlcnRpc2VkIGZpbGUgJy9zeXMvZGV2aWNlcy92aXJ0dWFsL2RtaS9pZC9wcm9kdWN0X3V1aWQnIGJlY2F1c2UgaXQgcmVxdWlyZXMgcm9vdCB0byByZWFkLlxuICAgICAgICAvLyBJbnN0ZWFkLCBzeXNfdmVuZG9yIGNvbnRhaW5zIHNvbWV0aGluZyBsaWtlICdBbWF6b24gRUMyJy5cbiAgICAgICAgWycvc3lzL2RldmljZXMvdmlydHVhbC9kbWkvaWQvc3lzX3ZlbmRvcicsIC9lYzIvaV0sXG4gICAgICBdO1xuICAgICAgZm9yIChjb25zdCBbZmlsZSwgcmVdIG9mIGZpbGVzKSB7XG4gICAgICAgIGlmIChtYXRjaGVzUmVnZXgocmUsIHJlYWRJZlBvc3NpYmxlKGZpbGUpKSkge1xuICAgICAgICAgIGluc3RhbmNlID0gdHJ1ZTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBkZWJ1ZyhpbnN0YW5jZSA/ICdMb29rcyBsaWtlIGFuIEVDMiBpbnN0YW5jZS4nIDogJ0RvZXMgbm90IGxvb2sgbGlrZSBhbiBFQzIgaW5zdGFuY2UuJyk7XG4gICAgaXNFYzJJbnN0YW5jZUNhY2hlID0gaW5zdGFuY2U7XG4gIH1cbiAgcmV0dXJuIGlzRWMySW5zdGFuY2VDYWNoZTtcbn1cblxubGV0IGlzRWMySW5zdGFuY2VDYWNoZTogYm9vbGVhbiB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZDtcblxuLyoqXG4gKiBBdHRlbXB0cyB0byBnZXQgYSBJbnN0YW5jZSBNZXRhZGF0YSBTZXJ2aWNlIFYyIHRva2VuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldEltZHNWMlRva2VuKG1ldGFkYXRhU2VydmljZTogQVdTLk1ldGFkYXRhU2VydmljZSk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGRlYnVnKCdBdHRlbXB0aW5nIHRvIHJldHJpZXZlIGFuIElNRFN2MiB0b2tlbi4nKTtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBtZXRhZGF0YVNlcnZpY2UucmVxdWVzdChcbiAgICAgICcvbGF0ZXN0L2FwaS90b2tlbicsXG4gICAgICB7XG4gICAgICAgIG1ldGhvZDogJ1BVVCcsXG4gICAgICAgIGhlYWRlcnM6IHsgJ3gtYXdzLWVjMi1tZXRhZGF0YS10b2tlbi10dGwtc2Vjb25kcyc6ICc2MCcgfSxcbiAgICAgIH0sXG4gICAgICAoZXJyOiBBV1MuQVdTRXJyb3IsIHRva2VuOiBzdHJpbmcgfCB1bmRlZmluZWQpID0+IHtcbiAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgIHJlamVjdChlcnIpO1xuICAgICAgICB9IGVsc2UgaWYgKCF0b2tlbikge1xuICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoJ0lNRFMgZGlkIG5vdCByZXR1cm4gYSB0b2tlbi4nKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVzb2x2ZSh0b2tlbik7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9KTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0cyB0byBnZXQgdGhlIHJlZ2lvbiBmcm9tIHRoZSBJbnN0YW5jZSBNZXRhZGF0YSBTZXJ2aWNlXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldFJlZ2lvbkZyb21JbWRzKG1ldGFkYXRhU2VydmljZTogQVdTLk1ldGFkYXRhU2VydmljZSwgdG9rZW46IHN0cmluZyB8IHVuZGVmaW5lZCk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGRlYnVnKCdSZXRyaWV2aW5nIHRoZSBBV1MgcmVnaW9uIGZyb20gdGhlIElNRFMuJyk7XG4gIGxldCBvcHRpb25zOiB7IG1ldGhvZD86IHN0cmluZyB8IHVuZGVmaW5lZDsgaGVhZGVycz86IHsgW2tleTogc3RyaW5nXTogc3RyaW5nOyB9IHwgdW5kZWZpbmVkOyB9ID0ge307XG4gIGlmICh0b2tlbikge1xuICAgIG9wdGlvbnMgPSB7IGhlYWRlcnM6IHsgJ3gtYXdzLWVjMi1tZXRhZGF0YS10b2tlbic6IHRva2VuIH0gfTtcbiAgfVxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIG1ldGFkYXRhU2VydmljZS5yZXF1ZXN0KFxuICAgICAgJy9sYXRlc3QvZHluYW1pYy9pbnN0YW5jZS1pZGVudGl0eS9kb2N1bWVudCcsXG4gICAgICBvcHRpb25zLFxuICAgICAgKGVycjogQVdTLkFXU0Vycm9yLCBpbnN0YW5jZUlkZW50aXR5RG9jdW1lbnQ6IHN0cmluZyB8IHVuZGVmaW5lZCkgPT4ge1xuICAgICAgICBpZiAoZXJyKSB7XG4gICAgICAgICAgcmVqZWN0KGVycik7XG4gICAgICAgIH0gZWxzZSBpZiAoIWluc3RhbmNlSWRlbnRpdHlEb2N1bWVudCkge1xuICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoJ0lNRFMgZGlkIG5vdCByZXR1cm4gYW4gSW5zdGFuY2UgSWRlbnRpdHkgRG9jdW1lbnQuJykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICByZXNvbHZlKEpTT04ucGFyc2UoaW5zdGFuY2VJZGVudGl0eURvY3VtZW50KS5yZWdpb24pO1xuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHJlamVjdChlKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gaG9tZURpcigpIHtcbiAgcmV0dXJuIHByb2Nlc3MuZW52LkhPTUUgfHwgcHJvY2Vzcy5lbnYuVVNFUlBST0ZJTEVcbiAgICB8fCAocHJvY2Vzcy5lbnYuSE9NRVBBVEggPyAoKHByb2Nlc3MuZW52LkhPTUVEUklWRSB8fCAnQzovJykgKyBwcm9jZXNzLmVudi5IT01FUEFUSCkgOiBudWxsKSB8fCBvcy5ob21lZGlyKCk7XG59XG5cbmZ1bmN0aW9uIGNyZWRlbnRpYWxzRmlsZU5hbWUoKSB7XG4gIHJldHVybiBwcm9jZXNzLmVudi5BV1NfU0hBUkVEX0NSRURFTlRJQUxTX0ZJTEUgfHwgcGF0aC5qb2luKGhvbWVEaXIoKSwgJy5hd3MnLCAnY3JlZGVudGlhbHMnKTtcbn1cblxuZnVuY3Rpb24gY29uZmlnRmlsZU5hbWUoKSB7XG4gIHJldHVybiBwcm9jZXNzLmVudi5BV1NfQ09ORklHX0ZJTEUgfHwgcGF0aC5qb2luKGhvbWVEaXIoKSwgJy5hd3MnLCAnY29uZmlnJyk7XG59XG5cbi8qKlxuICogRm9yY2UgdGhlIEpTIFNESyB0byBob25vciB0aGUgfi8uYXdzL2NvbmZpZyBmaWxlIChhbmQgdmFyaW91cyBzZXR0aW5ncyB0aGVyZWluKVxuICpcbiAqIEZvciBleGFtcGxlLCB0aGVyZSBpcyBqdXN0ICpOTyogd2F5IHRvIGRvIEFzc3VtZVJvbGUgY3JlZGVudGlhbHMgYXMgbG9uZyBhcyBBV1NfU0RLX0xPQURfQ09ORklHIGlzIG5vdCBzZXQsXG4gKiBvciByZWFkIGNyZWRlbnRpYWxzIGZyb20gdGhhdCBmaWxlLlxuICpcbiAqIFRoZSBTREsgY3Jhc2hlcyBpZiB0aGUgdmFyaWFibGUgaXMgc2V0IGJ1dCB0aGUgZmlsZSBkb2VzIG5vdCBleGlzdCwgc28gY29uZGl0aW9uYWxseSBzZXQgaXQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGZvcmNlU2RrVG9SZWFkQ29uZmlnSWZQcmVzZW50KCkge1xuICBpZiAoYXdhaXQgZnMucGF0aEV4aXN0cyhjb25maWdGaWxlTmFtZSgpKSkge1xuICAgIHByb2Nlc3MuZW52LkFXU19TREtfTE9BRF9DT05GSUcgPSAnMSc7XG4gIH1cbn1cblxuZnVuY3Rpb24gbWF0Y2hlc1JlZ2V4KHJlOiBSZWdFeHAsIHM6IHN0cmluZyB8IHVuZGVmaW5lZCkge1xuICByZXR1cm4gcyAhPT0gdW5kZWZpbmVkICYmIHJlLmV4ZWMocykgIT09IG51bGw7XG59XG5cbi8qKlxuICogUmVhZCBhIGZpbGUgaWYgaXQgZXhpc3RzLCBvciByZXR1cm4gdW5kZWZpbmVkXG4gKlxuICogTm90IGFzeW5jIGJlY2F1c2UgaXQgaXMgdXNlZCBpbiB0aGUgY29uc3RydWN0b3JcbiAqL1xuZnVuY3Rpb24gcmVhZElmUG9zc2libGUoZmlsZW5hbWU6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIHRyeSB7XG4gICAgaWYgKCFmcy5wYXRoRXhpc3RzU3luYyhmaWxlbmFtZSkpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfVxuICAgIHJldHVybiBmcy5yZWFkRmlsZVN5bmMoZmlsZW5hbWUsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSk7XG4gIH0gY2F0Y2ggKGU6IGFueSkge1xuICAgIGRlYnVnKGUpO1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuZXhwb3J0IGludGVyZmFjZSBDcmVkZW50aWFsQ2hhaW5PcHRpb25zIHtcbiAgcmVhZG9ubHkgcHJvZmlsZT86IHN0cmluZztcbiAgcmVhZG9ubHkgZWMyaW5zdGFuY2U/OiBib29sZWFuO1xuICByZWFkb25seSBjb250YWluZXJDcmVkcz86IGJvb2xlYW47XG4gIHJlYWRvbmx5IGh0dHBPcHRpb25zPzogQVdTLkhUVFBPcHRpb25zO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJlZ2lvbk9wdGlvbnMge1xuICByZWFkb25seSBwcm9maWxlPzogc3RyaW5nO1xuICByZWFkb25seSBlYzJpbnN0YW5jZT86IGJvb2xlYW47XG59XG5cbi8qKlxuICogQXNrIHVzZXIgZm9yIE1GQSB0b2tlbiBmb3IgZ2l2ZW4gc2VyaWFsXG4gKlxuICogUmVzdWx0IGlzIHNlbmQgdG8gY2FsbGJhY2sgZnVuY3Rpb24gZm9yIFNESyB0byBhdXRob3JpemUgdGhlIHJlcXVlc3RcbiAqL1xuYXN5bmMgZnVuY3Rpb24gdG9rZW5Db2RlRm4oc2VyaWFsQXJuOiBzdHJpbmcsIGNiOiAoZXJyPzogRXJyb3IsIHRva2VuPzogc3RyaW5nKSA9PiB2b2lkKTogUHJvbWlzZTx2b2lkPiB7XG4gIGRlYnVnKCdSZXF1aXJlIE1GQSB0b2tlbiBmb3Igc2VyaWFsIEFSTicsIHNlcmlhbEFybik7XG4gIHRyeSB7XG4gICAgY29uc3QgdG9rZW46IHN0cmluZyA9IGF3YWl0IHByb21wdGx5LnByb21wdChgTUZBIHRva2VuIGZvciAke3NlcmlhbEFybn06IGAsIHtcbiAgICAgIHRyaW06IHRydWUsXG4gICAgICBkZWZhdWx0OiAnJyxcbiAgICB9KTtcbiAgICBkZWJ1ZygnU3VjY2Vzc2Z1bGx5IGdvdCBNRkEgdG9rZW4gZnJvbSB1c2VyJyk7XG4gICAgY2IodW5kZWZpbmVkLCB0b2tlbik7XG4gIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgZGVidWcoJ0ZhaWxlZCB0byBnZXQgTUZBIHRva2VuJywgZXJyKTtcbiAgICBjYihlcnIpO1xuICB9XG59XG4iXX0=