cdk-serverless-clamscan
Version:
Serverless architecture to virus scan objects in Amazon S3.
409 lines (403 loc) • 65 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServerlessClamscan = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
const path = require("path");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
const aws_efs_1 = require("aws-cdk-lib/aws-efs");
const aws_events_1 = require("aws-cdk-lib/aws-events");
const aws_events_targets_1 = require("aws-cdk-lib/aws-events-targets");
const aws_iam_1 = require("aws-cdk-lib/aws-iam");
const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
const aws_lambda_destinations_1 = require("aws-cdk-lib/aws-lambda-destinations");
const aws_s3_1 = require("aws-cdk-lib/aws-s3");
const aws_s3_notifications_1 = require("aws-cdk-lib/aws-s3-notifications");
const aws_sqs_1 = require("aws-cdk-lib/aws-sqs");
const constructs_1 = require("constructs");
/**
An [aws-cdk](https://github.com/aws/aws-cdk) construct that uses [ClamAV®](https://www.clamav.net/).
to scan objects in Amazon S3 for viruses. The construct provides a flexible interface for a system
to act based on the results of a ClamAV virus scan.
The construct creates a Lambda function with EFS integration to support larger files.
A VPC with isolated subnets, a S3 Gateway endpoint will also be created.
Additionally creates an twice-daily job to download the latest ClamAV definition files to the
Virus Definitions S3 Bucket by utilizing an EventBridge rule and a Lambda function and
publishes CloudWatch Metrics to the 'serverless-clamscan' namespace.
__Important O&M__:
When ClamAV publishes updates to the scanner you will see “Your ClamAV installation is OUTDATED” in your scan results.
While the construct creates a system to keep the database definitions up to date, you must update the scanner to
detect all the latest Viruses.
Update the docker images of the Lambda functions with the latest version of ClamAV by re-running `cdk deploy`.
Successful Scan Event format
```json
{
"source": "serverless-clamscan",
"input_bucket": <input_bucket_name>,
"input_key": <object_key>,
"status": <"CLEAN"|"INFECTED"|"N/A">,
"message": <scan_summary>,
}
```
Note: The Virus Definitions bucket policy will likely cause a deletion error if you choose to delete
the stack associated in the construct. However since the bucket itself gets deleted, you can delete
the stack again to resolve the error.
*/
class ServerlessClamscan extends constructs_1.Construct {
/**
* Creates a ServerlessClamscan construct.
* @param scope The parent creating construct (usually `this`).
* @param id The construct's name.
* @param props A `ServerlessClamscanProps` interface.
*/
constructor(scope, id, props) {
super(scope, id);
this._efsRootPath = '/lambda';
this._efsMountPath = `/mnt${this._efsRootPath}`;
this._efsDefsPath = 'virus_database/';
this.useImportedBuckets = props.acceptResponsibilityForUsingImportedBucket;
if (!props.onResult) {
this.resultBus = new aws_events_1.EventBus(this, 'ScanResultBus');
this.resultDest = new aws_lambda_destinations_1.EventBridgeDestination(this.resultBus);
this.infectedRule = new aws_events_1.Rule(this, 'InfectedRule', {
eventBus: this.resultBus,
description: 'Event for when a file is marked INFECTED',
eventPattern: {
detail: {
responsePayload: {
source: ['serverless-clamscan'],
status: ['INFECTED'],
},
},
},
});
this.cleanRule = new aws_events_1.Rule(this, 'CleanRule', {
eventBus: this.resultBus,
description: 'Event for when a file is marked CLEAN',
eventPattern: {
detail: {
responsePayload: {
source: ['serverless-clamscan'],
status: ['CLEAN'],
},
},
},
});
}
else {
this.resultDest = props.onResult;
}
if (!props.onError) {
this.errorDeadLetterQueue = new aws_sqs_1.Queue(this, 'ScanErrorDeadLetterQueue', {
encryption: aws_sqs_1.QueueEncryption.KMS_MANAGED,
});
this.errorDeadLetterQueue.addToResourcePolicy(new aws_iam_1.PolicyStatement({
actions: ['sqs:*'],
effect: aws_iam_1.Effect.DENY,
principals: [new aws_iam_1.AnyPrincipal()],
conditions: { Bool: { 'aws:SecureTransport': false } },
resources: [this.errorDeadLetterQueue.queueArn],
}));
this.errorQueue = new aws_sqs_1.Queue(this, 'ScanErrorQueue', {
encryption: aws_sqs_1.QueueEncryption.KMS_MANAGED,
deadLetterQueue: {
maxReceiveCount: 3,
queue: this.errorDeadLetterQueue,
},
});
this.errorQueue.addToResourcePolicy(new aws_iam_1.PolicyStatement({
actions: ['sqs:*'],
effect: aws_iam_1.Effect.DENY,
principals: [new aws_iam_1.AnyPrincipal()],
conditions: { Bool: { 'aws:SecureTransport': false } },
resources: [this.errorQueue.queueArn],
}));
this.errorDest = new aws_lambda_destinations_1.SqsDestination(this.errorQueue);
}
else {
this.errorDest = props.onError;
}
const vpc = new aws_ec2_1.Vpc(this, 'ScanVPC', {
subnetConfiguration: [
{
subnetType: aws_ec2_1.SubnetType.PRIVATE_ISOLATED,
name: 'Isolated',
},
],
});
vpc.addFlowLog('FlowLogs');
this._s3Gw = vpc.addGatewayEndpoint('S3Endpoint', {
service: aws_ec2_1.GatewayVpcEndpointAwsService.S3,
});
const fileSystem = new aws_efs_1.FileSystem(this, 'ScanFileSystem', {
vpc: vpc,
encrypted: props.efsEncryption === false ? false : true,
lifecyclePolicy: aws_efs_1.LifecyclePolicy.AFTER_7_DAYS,
performanceMode: props.efsPerformanceMode ?? aws_efs_1.PerformanceMode.GENERAL_PURPOSE,
throughputMode: props.efsThroughputMode ?? aws_efs_1.ThroughputMode.BURSTING,
provisionedThroughputPerSecond: props.efsProvisionedThroughputPerSecond,
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
securityGroup: new aws_ec2_1.SecurityGroup(this, 'ScanFileSystemSecurityGroup', {
vpc: vpc,
allowAllOutbound: false,
}),
});
const lambda_ap = fileSystem.addAccessPoint('ScanLambdaAP', {
createAcl: {
ownerGid: '1000',
ownerUid: '1000',
permissions: '755',
},
posixUser: {
gid: '1000',
uid: '1000',
},
path: this._efsRootPath,
});
const logs_bucket = props.defsBucketAccessLogsConfig?.logsBucket;
const logs_bucket_prefix = props.defsBucketAccessLogsConfig?.logsPrefix;
if (logs_bucket === true || logs_bucket === undefined) {
this.defsAccessLogsBucket = new aws_s3_1.Bucket(this, 'VirusDefsAccessLogsBucket', {
encryption: aws_s3_1.BucketEncryption.S3_MANAGED,
removalPolicy: aws_cdk_lib_1.RemovalPolicy.RETAIN,
blockPublicAccess: {
blockPublicAcls: true,
blockPublicPolicy: true,
ignorePublicAcls: true,
restrictPublicBuckets: true,
},
objectOwnership: aws_s3_1.ObjectOwnership.OBJECT_WRITER,
});
this.defsAccessLogsBucket.addToResourcePolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.DENY,
actions: ['s3:*'],
resources: [
this.defsAccessLogsBucket.arnForObjects('*'),
this.defsAccessLogsBucket.bucketArn,
],
principals: [new aws_iam_1.AnyPrincipal()],
conditions: {
Bool: {
'aws:SecureTransport': false,
},
},
}));
}
else if (logs_bucket != false) {
this.defsAccessLogsBucket = logs_bucket;
}
const defs_bucket = new aws_s3_1.Bucket(this, 'VirusDefsBucket', {
encryption: aws_s3_1.BucketEncryption.S3_MANAGED,
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
serverAccessLogsBucket: this.defsAccessLogsBucket,
serverAccessLogsPrefix: logs_bucket === false ? undefined : logs_bucket_prefix,
blockPublicAccess: {
blockPublicAcls: true,
blockPublicPolicy: true,
ignorePublicAcls: true,
restrictPublicBuckets: true,
},
});
defs_bucket.addToResourcePolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.DENY,
actions: ['s3:*'],
resources: [defs_bucket.arnForObjects('*'), defs_bucket.bucketArn],
principals: [new aws_iam_1.AnyPrincipal()],
conditions: {
Bool: {
'aws:SecureTransport': false,
},
},
}));
defs_bucket.addToResourcePolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.ALLOW,
actions: ['s3:GetObject', 's3:ListBucket'],
resources: [defs_bucket.arnForObjects('*'), defs_bucket.bucketArn],
principals: [new aws_iam_1.AnyPrincipal()],
conditions: {
StringEquals: {
'aws:SourceVpce': this._s3Gw.vpcEndpointId,
},
},
}));
if (props.defsBucketAllowPolicyMutation !== true) {
defs_bucket.addToResourcePolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.DENY,
actions: ['s3:PutBucketPolicy', 's3:DeleteBucketPolicy'],
resources: [defs_bucket.bucketArn],
notPrincipals: [new aws_iam_1.AccountRootPrincipal()],
}));
}
this._s3Gw.addToPolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.ALLOW,
actions: ['s3:GetObject', 's3:ListBucket'],
resources: [defs_bucket.arnForObjects('*'), defs_bucket.bucketArn],
principals: [new aws_iam_1.AnyPrincipal()],
}));
this._scanFunction = new aws_lambda_1.DockerImageFunction(this, 'ServerlessClamscan', {
code: aws_lambda_1.DockerImageCode.fromImageAsset(path.join(__dirname, '../assets/lambda/code/scan'), {
buildArgs: {
// Only force update the docker layer cache once a day
CACHE_DATE: new Date().toDateString(),
},
extraHash: Date.now().toString(),
}),
onSuccess: this.resultDest,
onFailure: this.errorDest,
filesystem: aws_lambda_1.FileSystem.fromEfsAccessPoint(lambda_ap, this._efsMountPath),
vpc: vpc,
vpcSubnets: { subnets: vpc.isolatedSubnets },
allowAllOutbound: false,
timeout: props.scanFunctionTimeout ?? aws_cdk_lib_1.Duration.minutes(15),
memorySize: props.scanFunctionMemorySize ?? 10240,
reservedConcurrentExecutions: props.reservedConcurrency,
environment: {
EFS_MOUNT_PATH: this._efsMountPath,
EFS_DEF_PATH: this._efsDefsPath,
DEFS_URL: defs_bucket.virtualHostedUrlForObject(),
POWERTOOLS_METRICS_NAMESPACE: 'serverless-clamscan',
POWERTOOLS_SERVICE_NAME: 'virus-scan',
},
});
this._scanFunction.connections.allowToAnyIpv4(aws_ec2_1.Port.tcp(443), 'Allow outbound HTTPS traffic for S3 access.');
defs_bucket.grantRead(this._scanFunction);
const download_defs = new aws_lambda_1.DockerImageFunction(this, 'DownloadDefs', {
code: aws_lambda_1.DockerImageCode.fromImageAsset(path.join(__dirname, '../assets/lambda/code/download_defs'), {
buildArgs: {
// Only force update the docker layer cache once a day
CACHE_DATE: new Date().toDateString(),
},
extraHash: Date.now().toString(),
}),
timeout: aws_cdk_lib_1.Duration.minutes(5),
memorySize: 1024,
environment: {
DEFS_BUCKET: defs_bucket.bucketName,
POWERTOOLS_SERVICE_NAME: 'freshclam-update',
},
});
const stack = aws_cdk_lib_1.Stack.of(this);
if (download_defs.role) {
const download_defs_role = `arn:${stack.partition}:sts::${stack.account}:assumed-role/${download_defs.role.roleName}/${download_defs.functionName}`;
const download_defs_assumed_principal = new aws_iam_1.ArnPrincipal(download_defs_role);
defs_bucket.addToResourcePolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.DENY,
actions: ['s3:PutObject*'],
resources: [defs_bucket.arnForObjects('*')],
notPrincipals: [download_defs.role, download_defs_assumed_principal],
}));
defs_bucket.grantReadWrite(download_defs);
}
new aws_events_1.Rule(this, 'VirusDefsUpdateRule', {
schedule: aws_events_1.Schedule.rate(aws_cdk_lib_1.Duration.hours(12)),
targets: [new aws_events_targets_1.LambdaFunction(download_defs)],
});
const init_defs_cr = new aws_lambda_1.Function(this, 'InitDefs', {
runtime: aws_lambda_1.Runtime.PYTHON_3_9,
code: aws_lambda_1.Code.fromAsset(path.join(__dirname, '../assets/lambda/code/initialize_defs_cr')),
handler: 'lambda.lambda_handler',
timeout: aws_cdk_lib_1.Duration.minutes(5),
});
download_defs.grantInvoke(init_defs_cr);
new aws_cdk_lib_1.CustomResource(this, 'InitDefsCr', {
serviceToken: init_defs_cr.functionArn,
properties: {
FnName: download_defs.functionName,
},
});
if (props.buckets) {
props.buckets.forEach((bucket) => {
this.addSourceBucket(bucket);
});
}
}
/**
* @returns ArnPrincipal the ARN of the assumed role principal for the scan function
*/
get scanAssumedPrincipal() {
if (this._scanFunction.role) {
const stack = aws_cdk_lib_1.Stack.of(this);
const scan_assumed_role = `arn:${stack.partition}:sts::${stack.account}:assumed-role/${this._scanFunction.role.roleName}/${this._scanFunction.functionName}`;
return new aws_iam_1.ArnPrincipal(scan_assumed_role);
}
else {
throw new Error('The scan function role is undefined');
}
}
/**
* Returns the statement that should be added to the bucket policy
in order to prevent objects to be accessed when they are not clean
or there have been scanning errors: this policy should be added
manually if external buckets are passed to addSourceBucket()
* @param bucket The bucket which you need to protect with the policy
* @returns PolicyStatement the policy statement if available
*/
getPolicyStatementForBucket(bucket) {
if (this._scanFunction.role) {
const scan_assumed_principal = this.scanAssumedPrincipal;
return new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.DENY,
actions: ['s3:GetObject'],
resources: [bucket.arnForObjects('*')],
principals: [new aws_iam_1.AnyPrincipal()],
conditions: {
StringEquals: {
's3:ExistingObjectTag/scan-status': [
'IN PROGRESS',
'INFECTED',
'ERROR',
],
},
ArnNotEquals: {
'aws:PrincipalArn': [this._scanFunction.role.roleArn, scan_assumed_principal.arn],
},
},
});
}
else {
throw new Error("Can't generate a valid S3 bucket policy, the scan function role is undefined");
}
}
/**
* Sets the specified S3 Bucket as a s3:ObjectCreate* for the ClamAV function.
Grants the ClamAV function permissions to get and tag objects.
Adds a bucket policy to disallow GetObject operations on files that are tagged 'IN PROGRESS', 'INFECTED', or 'ERROR'.
* @param bucket The bucket to add the scanning bucket policy and s3:ObjectCreate* trigger to.
*/
addSourceBucket(bucket) {
bucket.addEventNotification(aws_s3_1.EventType.OBJECT_CREATED, new aws_s3_notifications_1.LambdaDestination(this._scanFunction));
bucket.grantRead(this._scanFunction);
this._scanFunction.addToRolePolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.ALLOW,
actions: ['s3:PutObjectTagging', 's3:PutObjectVersionTagging'],
resources: [bucket.arnForObjects('*')],
}));
if (this._scanFunction.role) {
const scan_assumed_principal = this.scanAssumedPrincipal;
this._s3Gw.addToPolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.ALLOW,
actions: ['s3:GetObject*', 's3:GetBucket*', 's3:List*'],
resources: [bucket.bucketArn, bucket.arnForObjects('*')],
principals: [this._scanFunction.role, scan_assumed_principal],
}));
this._s3Gw.addToPolicy(new aws_iam_1.PolicyStatement({
effect: aws_iam_1.Effect.ALLOW,
actions: ['s3:PutObjectTagging', 's3:PutObjectVersionTagging'],
resources: [bucket.arnForObjects('*')],
principals: [this._scanFunction.role, scan_assumed_principal],
}));
const result = bucket.addToResourcePolicy(this.getPolicyStatementForBucket(bucket));
if (!result.statementAdded && !this.useImportedBuckets) {
throw new Error('acceptResponsibilityForUsingImportedBucket must be set when adding an imported bucket. When using imported buckets the user is responsible for adding the required policy statement to the bucket policy: `getPolicyStatementForBucket()` can be used to retrieve the policy statement required by the solution');
}
}
}
}
exports.ServerlessClamscan = ServerlessClamscan;
_a = JSII_RTTI_SYMBOL_1;
ServerlessClamscan[_a] = { fqn: "cdk-serverless-clamscan.ServerlessClamscan", version: "2.10.77" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxxRUFBcUU7QUFDckUsc0NBQXNDO0FBRXRDLDZCQUE2QjtBQUM3Qiw2Q0FHcUI7QUFDckIsaURBSzZCO0FBQzdCLGlEQUFtRztBQUNuRyx1REFBa0U7QUFDbEUsdUVBQWdFO0FBQ2hFLGlEQUk2QjtBQUM3Qix1REFPZ0M7QUFDaEMsaUZBRzZDO0FBQzdDLCtDQUFtRztBQUNuRywyRUFBcUU7QUFDckUsaURBQTZEO0FBRTdELDJDQUF1QztBQThFdkM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWlDRztBQUNILE1BQWEsa0JBQW1CLFNBQVEsc0JBQVM7SUFvRC9DOzs7OztPQUtHO0lBQ0gsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUE4QjtRQUN0RSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBWFgsaUJBQVksR0FBRyxTQUFTLENBQUM7UUFDekIsa0JBQWEsR0FBRyxPQUFPLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUMzQyxpQkFBWSxHQUFHLGlCQUFpQixDQUFDO1FBV3ZDLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxLQUFLLENBQUMsMENBQTBDLENBQUM7UUFFM0UsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNwQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUkscUJBQVEsQ0FBQyxJQUFJLEVBQUUsZUFBZSxDQUFDLENBQUM7WUFDckQsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLGdEQUFzQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM3RCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksaUJBQUksQ0FBQyxJQUFJLEVBQUUsY0FBYyxFQUFFO2dCQUNqRCxRQUFRLEVBQUUsSUFBSSxDQUFDLFNBQVM7Z0JBQ3hCLFdBQVcs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QUE0QixDQUFDO2dCQUM5RCxTQUFTLEVBQUUsQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUN0QyxVQUFVLEVBQUUsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRSxzQkFBc0IsQ0FBQzthQUM5RCxDQUFDLENBQ0gsQ0FBQztZQUVGLE1BQU0sTUFBTSxHQUE4QixNQUFNLENBQUMsbUJBQW1CLENBQ2xFLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxNQUFNLENBQUMsQ0FDekMsQ0FBQztZQUVGLElBQUksQ0FBQyxNQUFNLENBQUMsY0FBYyxJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7Z0JBQ3ZELE1BQU0sSUFBSSxLQUFLLENBQUMsaVRBQWlULENBQUMsQ0FBQztZQUNyVSxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7O0FBN2NILGdEQThjQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCBBbWF6b24uY29tLCBJbmMuIG9yIGl0cyBhZmZpbGlhdGVzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7XG4gIEN1c3RvbVJlc291cmNlLCBEdXJhdGlvbiwgUmVtb3ZhbFBvbGljeSxcbiAgU3RhY2ssXG59IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7XG4gIEdhdGV3YXlWcGNFbmRwb2ludCxcbiAgR2F0ZXdheVZwY0VuZHBvaW50QXdzU2VydmljZSxcbiAgUG9ydCxcbiAgU2VjdXJpdHlHcm91cCwgU3VibmV0VHlwZSwgVnBjLFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWMyJztcbmltcG9ydCB7IEZpbGVTeXN0ZW0sIExpZmVjeWNsZVBvbGljeSwgUGVyZm9ybWFuY2VNb2RlLCBUaHJvdWdocHV0TW9kZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lZnMnO1xuaW1wb3J0IHsgRXZlbnRCdXMsIFJ1bGUsIFNjaGVkdWxlIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWV2ZW50cyc7XG5pbXBvcnQgeyBMYW1iZGFGdW5jdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1ldmVudHMtdGFyZ2V0cyc7XG5pbXBvcnQge1xuICBBY2NvdW50Um9vdFByaW5jaXBhbCxcbiAgQWRkVG9SZXNvdXJjZVBvbGljeVJlc3VsdCwgQW55UHJpbmNpcGFsLCBBcm5QcmluY2lwYWwsIEVmZmVjdCxcbiAgUG9saWN5U3RhdGVtZW50LFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcbmltcG9ydCB7XG4gIENvZGUsIERvY2tlckltYWdlQ29kZSxcbiAgRG9ja2VySW1hZ2VGdW5jdGlvbixcbiAgRnVuY3Rpb24sXG4gIElEZXN0aW5hdGlvbixcbiAgRmlsZVN5c3RlbSBhcyBMYW1iZGFGaWxlU3lzdGVtLFxuICBSdW50aW1lLFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCB7XG4gIEV2ZW50QnJpZGdlRGVzdGluYXRpb24sXG4gIFNxc0Rlc3RpbmF0aW9uLFxufSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhLWRlc3RpbmF0aW9ucyc7XG5pbXBvcnQgeyBCdWNrZXQsIEJ1Y2tldEVuY3J5cHRpb24sIEV2ZW50VHlwZSwgSUJ1Y2tldCwgT2JqZWN0T3duZXJzaGlwIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXMzJztcbmltcG9ydCB7IExhbWJkYURlc3RpbmF0aW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXMzLW5vdGlmaWNhdGlvbnMnO1xuaW1wb3J0IHsgUXVldWUsIFF1ZXVlRW5jcnlwdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zcXMnO1xuaW1wb3J0IHsgU2l6ZSB9IGZyb20gJ2F3cy1jZGstbGliL2NvcmUnO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5cbi8qKlxuICogSW50ZXJmYWNlIGZvciBTZXJ2ZXJsZXNzQ2xhbXNjYW4gVmlydXMgRGVmaW5pdGlvbnMgUzMgQnVja2V0IExvZ2dpbmcuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2VydmVybGVzc0NsYW1zY2FuTG9nZ2luZ1Byb3BzIHtcbiAgLyoqXG4gICAqIERlc3RpbmF0aW9uIGJ1Y2tldCBmb3IgdGhlIHNlcnZlciBhY2Nlc3MgbG9ncyAoRGVmYXVsdDogQ3JlYXRlcyBhIG5ldyBTMyBCdWNrZXQgZm9yIGFjY2VzcyBsb2dzKS5cbiAgICovXG4gIHJlYWRvbmx5IGxvZ3NCdWNrZXQ/OiBib29sZWFuIHwgSUJ1Y2tldDtcbiAgLyoqXG4gICAqIE9wdGlvbmFsIGxvZyBmaWxlIHByZWZpeCB0byB1c2UgZm9yIHRoZSBidWNrZXQncyBhY2Nlc3MgbG9ncywgb3B0aW9uIGlzIGlnbm9yZWQgaWYgbG9nc19idWNrZXQgaXMgc2V0IHRvIGZhbHNlLlxuICAgKi9cbiAgcmVhZG9ubHkgbG9nc1ByZWZpeD86IHN0cmluZztcbn1cblxuLyoqXG4gKiBJbnRlcmZhY2UgZm9yIGNyZWF0aW5nIGEgU2VydmVybGVzc0NsYW1zY2FuLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNlcnZlcmxlc3NDbGFtc2NhblByb3BzIHtcbiAgLyoqXG4gICAqIEFuIG9wdGlvbmFsIGxpc3Qgb2YgUzMgYnVja2V0cyB0byBjb25maWd1cmUgZm9yIENsYW1BViBWaXJ1cyBTY2FubmluZzsgYnVja2V0cyBjYW4gYmUgYWRkZWQgbGF0ZXIgYnkgY2FsbGluZyBhZGRTb3VyY2VCdWNrZXQuXG4gICAqL1xuICByZWFkb25seSBidWNrZXRzPzogSUJ1Y2tldFtdO1xuICAvKipcbiAgICogT3B0aW9uYWxseSBzZXQgYSByZXNlcnZlZCBjb25jdXJyZW5jeSBmb3IgdGhlIHZpcnVzIHNjYW5uaW5nIExhbWJkYS5cbiAgICogQHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vbGFtYmRhL2xhdGVzdC9vcGVyYXRvcmd1aWRlL3Jlc2VydmVkLWNvbmN1cnJlbmN5Lmh0bWxcbiAgICovXG4gIHJlYWRvbmx5IHJlc2VydmVkQ29uY3VycmVuY3k/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBPcHRpb25hbGx5IHNldCB0aGUgbWVtb3J5IGFsbG9jYXRpb24gZm9yIHRoZSBzY2FuIGZ1bmN0aW9uLiBOb3RlIHRoYXQgbG93IG1lbW9yeSBhbGxvY2F0aW9ucyBtYXkgY2F1c2UgZXJyb3JzLiAoRGVmYXVsdDogMTAyNDApLlxuICAgKiBAc2VlIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9sYW1iZGEvbGF0ZXN0L29wZXJhdG9yZ3VpZGUvY29tcHV0aW5nLXBvd2VyLmh0bWxcbiAgICovXG4gIHJlYWRvbmx5IHNjYW5GdW5jdGlvbk1lbW9yeVNpemU/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBPcHRpb25hbGx5IHNldCB0aGUgdGltZW91dCBmb3IgdGhlIHNjYW4gZnVuY3Rpb24uIChEZWZhdWx0OiAxNSBtaW51dGVzKS5cbiAgICovXG4gIHJlYWRvbmx5IHNjYW5GdW5jdGlvblRpbWVvdXQ/OiBEdXJhdGlvbjtcbiAgLyoqXG4gICAqIFRoZSBMYW1iZGEgRGVzdGluYXRpb24gZm9yIGZpbGVzIG1hcmtlZCAnQ0xFQU4nIG9yICdJTkZFQ1RFRCcgYmFzZWQgb24gdGhlIENsYW1BViBWaXJ1cyBzY2FuIG9yICdOL0EnIGZvciBzY2FucyB0cmlnZ2VyZWQgYnkgUzMgZm9sZGVyIGNyZWF0aW9uIGV2ZW50cyBtYXJrZWQgKERlZmF1bHQ6IENyZWF0ZXMgYW5kIHB1Ymxpc2hlcyB0byBhIG5ldyBFdmVudCBCcmlkZ2UgQnVzIGlmIHVuc3BlY2lmaWVkKS5cbiAgICovXG4gIHJlYWRvbmx5IG9uUmVzdWx0PzogSURlc3RpbmF0aW9uO1xuICAvKipcbiAgICogVGhlIExhbWJkYSBEZXN0aW5hdGlvbiBmb3IgZmlsZXMgdGhhdCBmYWlsIHRvIHNjYW4gYW5kIGFyZSBtYXJrZWQgJ0VSUk9SJyBvciBzdHVjayAnSU4gUFJPR1JFU1MnIGR1ZSB0byBhIExhbWJkYSB0aW1lb3V0IChEZWZhdWx0OiBDcmVhdGVzIGFuZCBwdWJsaXNoZXMgdG8gYSBuZXcgU1FTIHF1ZXVlIGlmIHVuc3BlY2lmaWVkKS5cbiAgICovXG4gIHJlYWRvbmx5IG9uRXJyb3I/OiBJRGVzdGluYXRpb247XG4gIC8qKlxuICAgKiBXaGV0aGVyIG9yIG5vdCB0byBlbmFibGUgZW5jcnlwdGlvbiBvbiBFRlMgZmlsZXN5c3RlbSAoRGVmYXVsdDogZW5hYmxlZCkuXG4gICAqL1xuICByZWFkb25seSBlZnNFbmNyeXB0aW9uPzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIFNldCB0aGUgcGVyZm9ybWFuY2UgbW9kZSBvZiB0aGUgRUZTIGZpbGUgc3lzdGVtIChEZWZhdWx0OiBHRU5FUkFMX1BVUlBPU0UpLlxuICAgKi9cbiAgcmVhZG9ubHkgZWZzUGVyZm9ybWFuY2VNb2RlPzogUGVyZm9ybWFuY2VNb2RlO1xuICAvKipcbiAgICogU2V0IHRoZSB0aHJvdWdocHV0IG1vZGUgb2YgdGhlIEVGUyBmaWxlIHN5c3RlbSAoRGVmYXVsdDogQlVSU1RJTkcpLlxuICAgKi9cbiAgcmVhZG9ubHkgZWZzVGhyb3VnaHB1dE1vZGU/OiBUaHJvdWdocHV0TW9kZTtcbiAgLyoqXG4gICAqIFByb3Zpc2lvbmVkIHRocm91Z2hwdXQgZm9yIHRoZSBFRlMgZmlsZSBzeXN0ZW0uIFRoaXMgaXMgYSByZXF1aXJlZCBwcm9wZXJ0eSBpZiB0aGUgdGhyb3VnaHB1dCBtb2RlIGlzIHNldCB0byBQUk9WSVNJT05FRC4gTXVzdCBiZSBhdCBsZWFzdCAxTWlCL3MgKERlZmF1bHQ6IG5vbmUpLlxuICAgKi9cbiAgcmVhZG9ubHkgZWZzUHJvdmlzaW9uZWRUaHJvdWdocHV0UGVyU2Vjb25kPzogU2l6ZTtcbiAgLyoqXG4gICAqIFdoZXRoZXIgb3Igbm90IHRvIGVuYWJsZSBBY2Nlc3MgTG9nZ2luZyBmb3IgdGhlIFZpcnVzIERlZmluaXRpb25zIGJ1Y2tldCwgeW91IGNhbiBzcGVjaWZ5IGFuIGV4aXN0aW5nIGJ1Y2tldCBhbmQgcHJlZml4IChEZWZhdWx0OiBDcmVhdGVzIGEgbmV3IFMzIEJ1Y2tldCBmb3IgYWNjZXNzIGxvZ3MpLlxuICAgKi9cbiAgcmVhZG9ubHkgZGVmc0J1Y2tldEFjY2Vzc0xvZ3NDb25maWc/OiBTZXJ2ZXJsZXNzQ2xhbXNjYW5Mb2dnaW5nUHJvcHM7XG4gIC8qKlxuICAgKiBBbGxvd3MgdGhlIHVzZSBvZiBpbXBvcnRlZCBidWNrZXRzLiBXaGVuIHVzaW5nIGltcG9ydGVkIGJ1Y2tldHMgdGhlIHVzZXIgaXMgcmVzcG9uc2libGUgZm9yIGFkZGluZyB0aGUgcmVxdWlyZWQgcG9saWN5IHN0YXRlbWVudCB0byB0aGUgYnVja2V0IHBvbGljeTogYGdldFBvbGljeVN0YXRlbWVudEZvckJ1Y2tldCgpYCBjYW4gYmUgdXNlZCB0byByZXRyaWV2ZSB0aGUgcG9saWN5IHN0YXRlbWVudCByZXF1aXJlZCBieSB0aGUgc29sdXRpb24uXG4gICAqL1xuICByZWFkb25seSBhY2NlcHRSZXNwb25zaWJpbGl0eUZvclVzaW5nSW1wb3J0ZWRCdWNrZXQ/OiBib29sZWFuO1xuICAvKipcbiAgICogQWxsb3cgZm9yIG5vbi1yb290IHVzZXJzIHRvIG1vZGlmeS9kZWxldGUgdGhlIGJ1Y2tldCBwb2xpY3kgb24gdGhlIFZpcnVzIERlZmluaXRpb25zIGJ1Y2tldC5cbiAgICogV2FybmluZzogY2hhbmdpbmcgdGhpcyBmbGFnIGZyb20gJ2ZhbHNlJyB0byAndHJ1ZScgb24gZXhpc3RpbmcgZGVwbG95bWVudHMgd2lsbCBjYXVzZSB1cGRhdGVzIHRvIGZhaWwuXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBkZWZzQnVja2V0QWxsb3dQb2xpY3lNdXRhdGlvbj86IGJvb2xlYW47XG59XG5cbi8qKlxuICBBbiBbYXdzLWNka10oaHR0cHM6Ly9naXRodWIuY29tL2F3cy9hd3MtY2RrKSBjb25zdHJ1Y3QgdGhhdCB1c2VzIFtDbGFtQVbCrl0oaHR0cHM6Ly93d3cuY2xhbWF2Lm5ldC8pLlxuICB0byBzY2FuIG9iamVjdHMgaW4gQW1hem9uIFMzIGZvciB2aXJ1c2VzLiBUaGUgY29uc3RydWN0IHByb3ZpZGVzIGEgZmxleGlibGUgaW50ZXJmYWNlIGZvciBhIHN5c3RlbVxuICB0byBhY3QgYmFzZWQgb24gdGhlIHJlc3VsdHMgb2YgYSBDbGFtQVYgdmlydXMgc2Nhbi5cblxuICBUaGUgY29uc3RydWN0IGNyZWF0ZXMgYSBMYW1iZGEgZnVuY3Rpb24gd2l0aCBFRlMgaW50ZWdyYXRpb24gdG8gc3VwcG9ydCBsYXJnZXIgZmlsZXMuXG4gIEEgVlBDIHdpdGggaXNvbGF0ZWQgc3VibmV0cywgYSBTMyBHYXRld2F5IGVuZHBvaW50IHdpbGwgYWxzbyBiZSBjcmVhdGVkLlxuXG4gIEFkZGl0aW9uYWxseSBjcmVhdGVzIGFuIHR3aWNlLWRhaWx5IGpvYiB0byBkb3dubG9hZCB0aGUgbGF0ZXN0IENsYW1BViBkZWZpbml0aW9uIGZpbGVzIHRvIHRoZVxuICBWaXJ1cyBEZWZpbml0aW9ucyBTMyBCdWNrZXQgYnkgdXRpbGl6aW5nIGFuIEV2ZW50QnJpZGdlIHJ1bGUgYW5kIGEgTGFtYmRhIGZ1bmN0aW9uIGFuZFxuICBwdWJsaXNoZXMgQ2xvdWRXYXRjaCBNZXRyaWNzIHRvIHRoZSAnc2VydmVybGVzcy1jbGFtc2NhbicgbmFtZXNwYWNlLlxuXG4gIF9fSW1wb3J0YW50IE8mTV9fOlxuICBXaGVuIENsYW1BViBwdWJsaXNoZXMgdXBkYXRlcyB0byB0aGUgc2Nhbm5lciB5b3Ugd2lsbCBzZWUg4oCcWW91ciBDbGFtQVYgaW5zdGFsbGF0aW9uIGlzIE9VVERBVEVE4oCdIGluIHlvdXIgc2NhbiByZXN1bHRzLlxuICBXaGlsZSB0aGUgY29uc3RydWN0IGNyZWF0ZXMgYSBzeXN0ZW0gdG8ga2VlcCB0aGUgZGF0YWJhc2UgZGVmaW5pdGlvbnMgdXAgdG8gZGF0ZSwgeW91IG11c3QgdXBkYXRlIHRoZSBzY2FubmVyIHRvXG4gIGRldGVjdCBhbGwgdGhlIGxhdGVzdCBWaXJ1c2VzLlxuXG4gIFVwZGF0ZSB0aGUgZG9ja2VyIGltYWdlcyBvZiB0aGUgTGFtYmRhIGZ1bmN0aW9ucyB3aXRoIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiBDbGFtQVYgYnkgcmUtcnVubmluZyBgY2RrIGRlcGxveWAuXG5cbiAgU3VjY2Vzc2Z1bCBTY2FuIEV2ZW50IGZvcm1hdFxuICBgYGBqc29uXG4gIHtcbiAgICAgXCJzb3VyY2VcIjogXCJzZXJ2ZXJsZXNzLWNsYW1zY2FuXCIsXG4gICAgIFwiaW5wdXRfYnVja2V0XCI6IDxpbnB1dF9idWNrZXRfbmFtZT4sXG4gICAgIFwiaW5wdXRfa2V5XCI6IDxvYmplY3Rfa2V5PixcbiAgICAgXCJzdGF0dXNcIjogPFwiQ0xFQU5cInxcIklORkVDVEVEXCJ8XCJOL0FcIj4sXG4gICAgIFwibWVzc2FnZVwiOiA8c2Nhbl9zdW1tYXJ5PixcbiAgIH1cbiAgYGBgXG5cbiAgTm90ZTogVGhlIFZpcnVzIERlZmluaXRpb25zIGJ1Y2tldCBwb2xpY3kgd2lsbCBsaWtlbHkgY2F1c2UgYSBkZWxldGlvbiBlcnJvciBpZiB5b3UgY2hvb3NlIHRvIGRlbGV0ZVxuICB0aGUgc3RhY2sgYXNzb2NpYXRlZCBpbiB0aGUgY29uc3RydWN0LiBIb3dldmVyIHNpbmNlIHRoZSBidWNrZXQgaXRzZWxmIGdldHMgZGVsZXRlZCwgeW91IGNhbiBkZWxldGVcbiAgdGhlIHN0YWNrIGFnYWluIHRvIHJlc29sdmUgdGhlIGVycm9yLlxuICovXG5leHBvcnQgY2xhc3MgU2VydmVybGVzc0NsYW1zY2FuIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgLyoqXG4gICAgVGhlIExhbWJkYSBEZXN0aW5hdGlvbiBmb3IgZmFpbGVkIG9uIGVycmVkIHNjYW5zIFtFUlJPUiwgSU4gUFJPR1JFU1MgKElmIGVycm9yIGlzIGR1ZSB0byBMYW1iZGEgdGltZW91dCldLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGVycm9yRGVzdDogSURlc3RpbmF0aW9uO1xuXG4gIC8qKlxuICAgIFRoZSBMYW1iZGEgRGVzdGluYXRpb24gZm9yIGNvbXBsZXRlZCBDbGFtQVYgc2NhbnMgW0NMRUFOLCBJTkZFQ1RFRF0uXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcmVzdWx0RGVzdDogSURlc3RpbmF0aW9uO1xuXG4gIC8qKlxuICAgIENvbmRpdGlvbmFsOiBUaGUgU1FTIFF1ZXVlIGZvciBlcnJlZCBzY2FucyBpZiBhIGZhaWx1cmUgKG9uRXJyb3IpIGRlc3RpbmF0aW9uIHdhcyBub3Qgc3BlY2lmaWVkLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGVycm9yUXVldWU/OiBRdWV1ZTtcblxuICAvKipcbiAgICBDb25kaXRpb25hbDogVGhlIFNRUyBEZWFkIExldHRlciBRdWV1ZSBmb3IgdGhlIGVycm9yUXVldWUgaWYgYSBmYWlsdXJlIChvbkVycm9yKSBkZXN0aW5hdGlvbiB3YXMgbm90IHNwZWNpZmllZC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBlcnJvckRlYWRMZXR0ZXJRdWV1ZT86IFF1ZXVlO1xuXG4gIC8qKlxuICAgIENvbmRpdGlvbmFsOiBUaGUgRXZlbnQgQnJpZGdlIEJ1cyBmb3IgY29tcGxldGVkIENsYW1BViBzY2FucyBpZiBhIHN1Y2Nlc3MgKG9uUmVzdWx0KSBkZXN0aW5hdGlvbiB3YXMgbm90IHNwZWNpZmllZC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSByZXN1bHRCdXM/OiBFdmVudEJ1cztcblxuICAvKipcbiAgICBDb25kaXRpb25hbDogQW4gRXZlbnQgQnJpZGdlIFJ1bGUgZm9yIGZpbGVzIHRoYXQgYXJlIG1hcmtlZCAnQ0xFQU4nIGJ5IENsYW1BViBpZiBhIHN1Y2Nlc3MgZGVzdGluYXRpb24gd2FzIG5vdCBzcGVjaWZpZWQuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgY2xlYW5SdWxlPzogUnVsZTtcblxuICAvKipcbiAgICBDb25kaXRpb25hbDogQW4gRXZlbnQgQnJpZGdlIFJ1bGUgZm9yIGZpbGVzIHRoYXQgYXJlIG1hcmtlZCAnSU5GRUNURUQnIGJ5IENsYW1BViBpZiBhIHN1Y2Nlc3MgZGVzdGluYXRpb24gd2FzIG5vdCBzcGVjaWZpZWQuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgaW5mZWN0ZWRSdWxlPzogUnVsZTtcblxuICAvKipcbiAgICBDb25kaXRpb25hbDogVGhlIEJ1Y2tldCBmb3IgYWNjZXNzIGxvZ3MgZm9yIHRoZSB2aXJ1cyBkZWZpbml0aW9ucyBidWNrZXQgaWYgbG9nZ2luZyBpcyBlbmFibGVkIChkZWZzQnVja2V0QWNjZXNzTG9nc0NvbmZpZykuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZGVmc0FjY2Vzc0xvZ3NCdWNrZXQ/OiBJQnVja2V0O1xuXG4gIC8qKlxuICAgIENvbmRpdGlvbmFsOiBXaGVuIHRydWUsIHRoZSB1c2VyIGFjY2VwdGVkIHRoZSByZXNwb25zaWJpbGl0eSBmb3IgdXNpbmcgaW1wb3J0ZWQgYnVja2V0cy5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB1c2VJbXBvcnRlZEJ1Y2tldHM/OiBib29sZWFuO1xuXG4gIHByaXZhdGUgX3NjYW5GdW5jdGlvbjogRG9ja2VySW1hZ2VGdW5jdGlvbjtcbiAgcHJpdmF0ZSBfczNHdzogR2F0ZXdheVZwY0VuZHBvaW50O1xuICBwcml2YXRlIF9lZnNSb290UGF0aCA9ICcvbGFtYmRhJztcbiAgcHJpdmF0ZSBfZWZzTW91bnRQYXRoID0gYC9tbnQke3RoaXMuX2Vmc1Jvb3RQYXRofWA7XG4gIHByaXZhdGUgX2Vmc0RlZnNQYXRoID0gJ3ZpcnVzX2RhdGFiYXNlLyc7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBTZXJ2ZXJsZXNzQ2xhbXNjYW4gY29uc3RydWN0LlxuICAgKiBAcGFyYW0gc2NvcGUgVGhlIHBhcmVudCBjcmVhdGluZyBjb25zdHJ1Y3QgKHVzdWFsbHkgYHRoaXNgKS5cbiAgICogQHBhcmFtIGlkIFRoZSBjb25zdHJ1Y3QncyBuYW1lLlxuICAgKiBAcGFyYW0gcHJvcHMgQSBgU2VydmVybGVzc0NsYW1zY2FuUHJvcHNgIGludGVyZmFjZS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBTZXJ2ZXJsZXNzQ2xhbXNjYW5Qcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICB0aGlzLnVzZUltcG9ydGVkQnVja2V0cyA9IHByb3BzLmFjY2VwdFJlc3BvbnNpYmlsaXR5Rm9yVXNpbmdJbXBvcnRlZEJ1Y2tldDtcblxuICAgIGlmICghcHJvcHMub25SZXN1bHQpIHtcbiAgICAgIHRoaXMucmVzdWx0QnVzID0gbmV3IEV2ZW50QnVzKHRoaXMsICdTY2FuUmVzdWx0QnVzJyk7XG4gICAgICB0aGlzLnJlc3VsdERlc3QgPSBuZXcgRXZlbnRCcmlkZ2VEZXN0aW5hdGlvbih0aGlzLnJlc3VsdEJ1cyk7XG4gICAgICB0aGlzLmluZmVjdGVkUnVsZSA9IG5ldyBSdWxlKHRoaXMsICdJbmZlY3RlZFJ1bGUnLCB7XG4gICAgICAgIGV2ZW50QnVzOiB0aGlzLnJlc3VsdEJ1cyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdFdmVudCBmb3Igd2hlbiBhIGZpbGUgaXMgbWFya2VkIElORkVDVEVEJyxcbiAgICAgICAgZXZlbnRQYXR0ZXJuOiB7XG4gICAgICAgICAgZGV0YWlsOiB7XG4gICAgICAgICAgICByZXNwb25zZVBheWxvYWQ6IHtcbiAgICAgICAgICAgICAgc291cmNlOiBbJ3NlcnZlcmxlc3MtY2xhbXNjYW4nXSxcbiAgICAgICAgICAgICAgc3RhdHVzOiBbJ0lORkVDVEVEJ10sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICAgIHRoaXMuY2xlYW5SdWxlID0gbmV3IFJ1bGUodGhpcywgJ0NsZWFuUnVsZScsIHtcbiAgICAgICAgZXZlbnRCdXM6IHRoaXMucmVzdWx0QnVzLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ0V2ZW50IGZvciB3aGVuIGEgZmlsZSBpcyBtYXJrZWQgQ0xFQU4nLFxuICAgICAgICBldmVudFBhdHRlcm46IHtcbiAgICAgICAgICBkZXRhaWw6IHtcbiAgICAgICAgICAgIHJlc3BvbnNlUGF5bG9hZDoge1xuICAgICAgICAgICAgICBzb3VyY2U6IFsnc2VydmVybGVzcy1jbGFtc2NhbiddLFxuICAgICAgICAgICAgICBzdGF0dXM6IFsnQ0xFQU4nXSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJlc3VsdERlc3QgPSBwcm9wcy5vblJlc3VsdDtcbiAgICB9XG5cbiAgICBpZiAoIXByb3BzLm9uRXJyb3IpIHtcbiAgICAgIHRoaXMuZXJyb3JEZWFkTGV0dGVyUXVldWUgPSBuZXcgUXVldWUodGhpcywgJ1NjYW5FcnJvckRlYWRMZXR0ZXJRdWV1ZScsIHtcbiAgICAgICAgZW5jcnlwdGlvbjogUXVldWVFbmNyeXB0aW9uLktNU19NQU5BR0VELFxuICAgICAgfSk7XG4gICAgICB0aGlzLmVycm9yRGVhZExldHRlclF1ZXVlLmFkZFRvUmVzb3VyY2VQb2xpY3kobmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIGFjdGlvbnM6IFsnc3FzOionXSxcbiAgICAgICAgZWZmZWN0OiBFZmZlY3QuREVOWSxcbiAgICAgICAgcHJpbmNpcGFsczogW25ldyBBbnlQcmluY2lwYWwoKV0sXG4gICAgICAgIGNvbmRpdGlvbnM6IHsgQm9vbDogeyAnYXdzOlNlY3VyZVRyYW5zcG9ydCc6IGZhbHNlIH0gfSxcbiAgICAgICAgcmVzb3VyY2VzOiBbdGhpcy5lcnJvckRlYWRMZXR0ZXJRdWV1ZS5xdWV1ZUFybl0sXG4gICAgICB9KSk7XG4gICAgICB0aGlzLmVycm9yUXVldWUgPSBuZXcgUXVldWUodGhpcywgJ1NjYW5FcnJvclF1ZXVlJywge1xuICAgICAgICBlbmNyeXB0aW9uOiBRdWV1ZUVuY3J5cHRpb24uS01TX01BTkFHRUQsXG4gICAgICAgIGRlYWRMZXR0ZXJRdWV1ZToge1xuICAgICAgICAgIG1heFJlY2VpdmVDb3VudDogMyxcbiAgICAgICAgICBxdWV1ZTogdGhpcy5lcnJvckRlYWRMZXR0ZXJRdWV1ZSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgICAgdGhpcy5lcnJvclF1ZXVlLmFkZFRvUmVzb3VyY2VQb2xpY3kobmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIGFjdGlvbnM6IFsnc3FzOionXSxcbiAgICAgICAgZWZmZWN0OiBFZmZlY3QuREVOWSxcbiAgICAgICAgcHJpbmNpcGFsczogW25ldyBBbnlQcmluY2lwYWwoKV0sXG4gICAgICAgIGNvbmRpdGlvbnM6IHsgQm9vbDogeyAnYXdzOlNlY3VyZVRyYW5zcG9ydCc6IGZhbHNlIH0gfSxcbiAgICAgICAgcmVzb3VyY2VzOiBbdGhpcy5lcnJvclF1ZXVlLnF1ZXVlQXJuXSxcbiAgICAgIH0pKTtcbiAgICAgIHRoaXMuZXJyb3JEZXN0ID0gbmV3IFNxc0Rlc3RpbmF0aW9uKHRoaXMuZXJyb3JRdWV1ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZXJyb3JEZXN0ID0gcHJvcHMub25FcnJvcjtcbiAgICB9XG5cbiAgICBjb25zdCB2cGMgPSBuZXcgVnBjKHRoaXMsICdTY2FuVlBDJywge1xuICAgICAgc3VibmV0Q29uZmlndXJhdGlvbjogW1xuICAgICAgICB7XG4gICAgICAgICAgc3VibmV0VHlwZTogU3VibmV0VHlwZS5QUklWQVRFX0lTT0xBVEVELFxuICAgICAgICAgIG5hbWU6ICdJc29sYXRlZCcsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuXG4gICAgdnBjLmFkZEZsb3dMb2coJ0Zsb3dMb2dzJyk7XG5cbiAgICB0aGlzLl9zM0d3ID0gdnBjLmFkZEdhdGV3YXlFbmRwb2ludCgnUzNFbmRwb2ludCcsIHtcbiAgICAgIHNlcnZpY2U6IEdhdGV3YXlWcGNFbmRwb2ludEF3c1NlcnZpY2UuUzMsXG4gICAgfSk7XG5cbiAgICBjb25zdCBmaWxlU3lzdGVtID0gbmV3IEZpbGVTeXN0ZW0odGhpcywgJ1NjYW5GaWxlU3lzdGVtJywge1xuICAgICAgdnBjOiB2cGMsXG4gICAgICBlbmNyeXB0ZWQ6IHByb3BzLmVmc0VuY3J5cHRpb24gPT09IGZhbHNlID8gZmFsc2UgOiB0cnVlLFxuICAgICAgbGlmZWN5Y2xlUG9saWN5OiBMaWZlY3ljbGVQb2xpY3kuQUZURVJfN19EQVlTLFxuICAgICAgcGVyZm9ybWFuY2VNb2RlOiBwcm9wcy5lZnNQZXJmb3JtYW5jZU1vZGUgPz8gUGVyZm9ybWFuY2VNb2RlLkdFTkVSQUxfUFVSUE9TRSxcbiAgICAgIHRocm91Z2hwdXRNb2RlOiBwcm9wcy5lZnNUaHJvdWdocHV0TW9kZSA/PyBUaHJvdWdocHV0TW9kZS5CVVJTVElORyxcbiAgICAgIHByb3Zpc2lvbmVkVGhyb3VnaHB1dFBlclNlY29uZDogcHJvcHMuZWZzUHJvd