UNPKG

cdk-serverless-clamscan

Version:

Serverless architecture to virus scan objects in Amazon S3.

409 lines (403 loc) 65.3 kB
"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: '0', ownerUid: '0', permissions: '755', }, posixUser: { gid: '0', uid: '0', }, 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:GetObjectTagging', 's3:GetObjectVersionTagging', '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:GetObjectTagging', 's3:GetObjectVersionTagging', '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.12.1" }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxxRUFBcUU7QUFDckUsc0NBQXNDO0FBRXRDLDZCQUE2QjtBQUM3Qiw2Q0FHcUI7QUFDckIsaURBSzZCO0FBQzdCLGlEQUFtRztBQUNuRyx1REFBa0U7QUFDbEUsdUVBQWdFO0FBQ2hFLGlEQUk2QjtBQUM3Qix1REFPZ0M7QUFDaEMsaUZBRzZDO0FBQzdDLCtDQUFtRztBQUNuRywyRUFBcUU7QUFDckUsaURBQTZEO0FBRTdELDJDQUF1QztBQThFdkM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWlDRztBQUNILE1BQWEsa0JBQW1CLFNBQVEsc0JBQVM7SUFvRC9DOzs7OztPQUtHO0lBQ0gsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUE4QjtRQUN0RSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBWFgsaUJBQVksR0FBRyxTQUFTLENBQUM7UUFDekIsa0JBQWEsR0FBRyxPQUFPLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUMzQyxpQkFBWSxHQUFHLGlCQUFpQixDQUFDO1FBV3ZDLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxLQUFLLENBQUMsMENBQTBDLENBQUM7UUFFM0UsSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNwQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUkscUJBQVEsQ0FBQyxJQUFJLEVBQUUsZUFBZSxDQUFDLENBQUM7WUFDckQsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLGdEQUFzQixDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM3RCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksaUJBQUksQ0FBQyxJQUFJLEVBQUUsY0FBYyxFQUFFO2dCQUNqRCxRQUFRLEVBQUUsSUFBSSxDQUFDLFNBQVM7Z0JBQ3hCLFdBQVcs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QUUsQ0FBQyxxQkFBcUIsRUFBRSw0QkFBNEIsRUFBRSxxQkFBcUIsRUFBRSw0QkFBNEIsQ0FBQztnQkFDbkgsU0FBUyxFQUFFLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDdEMsVUFBVSxFQUFFLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxJQUFJLEVBQUUsc0JBQXNCLENBQUM7YUFDOUQsQ0FBQyxDQUNILENBQUM7WUFFRixNQUFNLE1BQU0sR0FBOEIsTUFBTSxDQUFDLG1CQUFtQixDQUNsRSxJQUFJLENBQUMsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQ3pDLENBQUM7WUFFRixJQUFJLENBQUMsTUFBTSxDQUFDLGNBQWMsSUFBSSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO2dCQUN2RCxNQUFNLElBQUksS0FBSyxDQUFDLGlUQUFpVCxDQUFDLENBQUM7WUFDclUsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDOztBQTdjSCxnREE4Y0MiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgQW1hem9uLmNvbSwgSW5jLiBvciBpdHMgYWZmaWxpYXRlcy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQge1xuICBDdXN0b21SZXNvdXJjZSwgRHVyYXRpb24sIFJlbW92YWxQb2xpY3ksXG4gIFN0YWNrLFxufSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQge1xuICBHYXRld2F5VnBjRW5kcG9pbnQsXG4gIEdhdGV3YXlWcGNFbmRwb2ludEF3c1NlcnZpY2UsXG4gIFBvcnQsXG4gIFNlY3VyaXR5R3JvdXAsIFN1Ym5ldFR5cGUsIFZwYyxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWVjMic7XG5pbXBvcnQgeyBGaWxlU3lzdGVtLCBMaWZlY3ljbGVQb2xpY3ksIFBlcmZvcm1hbmNlTW9kZSwgVGhyb3VnaHB1dE1vZGUgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWZzJztcbmltcG9ydCB7IEV2ZW50QnVzLCBSdWxlLCBTY2hlZHVsZSB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1ldmVudHMnO1xuaW1wb3J0IHsgTGFtYmRhRnVuY3Rpb24gfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZXZlbnRzLXRhcmdldHMnO1xuaW1wb3J0IHtcbiAgQWNjb3VudFJvb3RQcmluY2lwYWwsXG4gIEFkZFRvUmVzb3VyY2VQb2xpY3lSZXN1bHQsIEFueVByaW5jaXBhbCwgQXJuUHJpbmNpcGFsLCBFZmZlY3QsXG4gIFBvbGljeVN0YXRlbWVudCxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWlhbSc7XG5pbXBvcnQge1xuICBDb2RlLCBEb2NrZXJJbWFnZUNvZGUsXG4gIERvY2tlckltYWdlRnVuY3Rpb24sXG4gIEZ1bmN0aW9uLFxuICBJRGVzdGluYXRpb24sXG4gIEZpbGVTeXN0ZW0gYXMgTGFtYmRhRmlsZVN5c3RlbSxcbiAgUnVudGltZSxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQge1xuICBFdmVudEJyaWRnZURlc3RpbmF0aW9uLFxuICBTcXNEZXN0aW5hdGlvbixcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYS1kZXN0aW5hdGlvbnMnO1xuaW1wb3J0IHsgQnVja2V0LCBCdWNrZXRFbmNyeXB0aW9uLCBFdmVudFR5cGUsIElCdWNrZXQsIE9iamVjdE93bmVyc2hpcCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMyc7XG5pbXBvcnQgeyBMYW1iZGFEZXN0aW5hdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMy1ub3RpZmljYXRpb25zJztcbmltcG9ydCB7IFF1ZXVlLCBRdWV1ZUVuY3J5cHRpb24gfSBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc3FzJztcbmltcG9ydCB7IFNpemUgfSBmcm9tICdhd3MtY2RrLWxpYi9jb3JlJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuXG4vKipcbiAqIEludGVyZmFjZSBmb3IgU2VydmVybGVzc0NsYW1zY2FuIFZpcnVzIERlZmluaXRpb25zIFMzIEJ1Y2tldCBMb2dnaW5nLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNlcnZlcmxlc3NDbGFtc2NhbkxvZ2dpbmdQcm9wcyB7XG4gIC8qKlxuICAgKiBEZXN0aW5hdGlvbiBidWNrZXQgZm9yIHRoZSBzZXJ2ZXIgYWNjZXNzIGxvZ3MgKERlZmF1bHQ6IENyZWF0ZXMgYSBuZXcgUzMgQnVja2V0IGZvciBhY2Nlc3MgbG9ncykuXG4gICAqL1xuICByZWFkb25seSBsb2dzQnVja2V0PzogYm9vbGVhbiB8IElCdWNrZXQ7XG4gIC8qKlxuICAgKiBPcHRpb25hbCBsb2cgZmlsZSBwcmVmaXggdG8gdXNlIGZvciB0aGUgYnVja2V0J3MgYWNjZXNzIGxvZ3MsIG9wdGlvbiBpcyBpZ25vcmVkIGlmIGxvZ3NfYnVja2V0IGlzIHNldCB0byBmYWxzZS5cbiAgICovXG4gIHJlYWRvbmx5IGxvZ3NQcmVmaXg/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogSW50ZXJmYWNlIGZvciBjcmVhdGluZyBhIFNlcnZlcmxlc3NDbGFtc2Nhbi5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTZXJ2ZXJsZXNzQ2xhbXNjYW5Qcm9wcyB7XG4gIC8qKlxuICAgKiBBbiBvcHRpb25hbCBsaXN0IG9mIFMzIGJ1Y2tldHMgdG8gY29uZmlndXJlIGZvciBDbGFtQVYgVmlydXMgU2Nhbm5pbmc7IGJ1Y2tldHMgY2FuIGJlIGFkZGVkIGxhdGVyIGJ5IGNhbGxpbmcgYWRkU291cmNlQnVja2V0LlxuICAgKi9cbiAgcmVhZG9ubHkgYnVja2V0cz86IElCdWNrZXRbXTtcbiAgLyoqXG4gICAqIE9wdGlvbmFsbHkgc2V0IGEgcmVzZXJ2ZWQgY29uY3VycmVuY3kgZm9yIHRoZSB2aXJ1cyBzY2FubmluZyBMYW1iZGEuXG4gICAqIEBzZWUgaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2xhbWJkYS9sYXRlc3Qvb3BlcmF0b3JndWlkZS9yZXNlcnZlZC1jb25jdXJyZW5jeS5odG1sXG4gICAqL1xuICByZWFkb25seSByZXNlcnZlZENvbmN1cnJlbmN5PzogbnVtYmVyO1xuICAvKipcbiAgICogT3B0aW9uYWxseSBzZXQgdGhlIG1lbW9yeSBhbGxvY2F0aW9uIGZvciB0aGUgc2NhbiBmdW5jdGlvbi4gTm90ZSB0aGF0IGxvdyBtZW1vcnkgYWxsb2NhdGlvbnMgbWF5IGNhdXNlIGVycm9ycy4gKERlZmF1bHQ6IDEwMjQwKS5cbiAgICogQHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vbGFtYmRhL2xhdGVzdC9vcGVyYXRvcmd1aWRlL2NvbXB1dGluZy1wb3dlci5odG1sXG4gICAqL1xuICByZWFkb25seSBzY2FuRnVuY3Rpb25NZW1vcnlTaXplPzogbnVtYmVyO1xuICAvKipcbiAgICogT3B0aW9uYWxseSBzZXQgdGhlIHRpbWVvdXQgZm9yIHRoZSBzY2FuIGZ1bmN0aW9uLiAoRGVmYXVsdDogMTUgbWludXRlcykuXG4gICAqL1xuICByZWFkb25seSBzY2FuRnVuY3Rpb25UaW1lb3V0PzogRHVyYXRpb247XG4gIC8qKlxuICAgKiBUaGUgTGFtYmRhIERlc3RpbmF0aW9uIGZvciBmaWxlcyBtYXJrZWQgJ0NMRUFOJyBvciAnSU5GRUNURUQnIGJhc2VkIG9uIHRoZSBDbGFtQVYgVmlydXMgc2NhbiBvciAnTi9BJyBmb3Igc2NhbnMgdHJpZ2dlcmVkIGJ5IFMzIGZvbGRlciBjcmVhdGlvbiBldmVudHMgbWFya2VkIChEZWZhdWx0OiBDcmVhdGVzIGFuZCBwdWJsaXNoZXMgdG8gYSBuZXcgRXZlbnQgQnJpZGdlIEJ1cyBpZiB1bnNwZWNpZmllZCkuXG4gICAqL1xuICByZWFkb25seSBvblJlc3VsdD86IElEZXN0aW5hdGlvbjtcbiAgLyoqXG4gICAqIFRoZSBMYW1iZGEgRGVzdGluYXRpb24gZm9yIGZpbGVzIHRoYXQgZmFpbCB0byBzY2FuIGFuZCBhcmUgbWFya2VkICdFUlJPUicgb3Igc3R1Y2sgJ0lOIFBST0dSRVNTJyBkdWUgdG8gYSBMYW1iZGEgdGltZW91dCAoRGVmYXVsdDogQ3JlYXRlcyBhbmQgcHVibGlzaGVzIHRvIGEgbmV3IFNRUyBxdWV1ZSBpZiB1bnNwZWNpZmllZCkuXG4gICAqL1xuICByZWFkb25seSBvbkVycm9yPzogSURlc3RpbmF0aW9uO1xuICAvKipcbiAgICogV2hldGhlciBvciBub3QgdG8gZW5hYmxlIGVuY3J5cHRpb24gb24gRUZTIGZpbGVzeXN0ZW0gKERlZmF1bHQ6IGVuYWJsZWQpLlxuICAgKi9cbiAgcmVhZG9ubHkgZWZzRW5jcnlwdGlvbj86IGJvb2xlYW47XG4gIC8qKlxuICAgKiBTZXQgdGhlIHBlcmZvcm1hbmNlIG1vZGUgb2YgdGhlIEVGUyBmaWxlIHN5c3RlbSAoRGVmYXVsdDogR0VORVJBTF9QVVJQT1NFKS5cbiAgICovXG4gIHJlYWRvbmx5IGVmc1BlcmZvcm1hbmNlTW9kZT86IFBlcmZvcm1hbmNlTW9kZTtcbiAgLyoqXG4gICAqIFNldCB0aGUgdGhyb3VnaHB1dCBtb2RlIG9mIHRoZSBFRlMgZmlsZSBzeXN0ZW0gKERlZmF1bHQ6IEJVUlNUSU5HKS5cbiAgICovXG4gIHJlYWRvbmx5IGVmc1Rocm91Z2hwdXRNb2RlPzogVGhyb3VnaHB1dE1vZGU7XG4gIC8qKlxuICAgKiBQcm92aXNpb25lZCB0aHJvdWdocHV0IGZvciB0aGUgRUZTIGZpbGUgc3lzdGVtLiBUaGlzIGlzIGEgcmVxdWlyZWQgcHJvcGVydHkgaWYgdGhlIHRocm91Z2hwdXQgbW9kZSBpcyBzZXQgdG8gUFJPVklTSU9ORUQuIE11c3QgYmUgYXQgbGVhc3QgMU1pQi9zIChEZWZhdWx0OiBub25lKS5cbiAgICovXG4gIHJlYWRvbmx5IGVmc1Byb3Zpc2lvbmVkVGhyb3VnaHB1dFBlclNlY29uZD86IFNpemU7XG4gIC8qKlxuICAgKiBXaGV0aGVyIG9yIG5vdCB0byBlbmFibGUgQWNjZXNzIExvZ2dpbmcgZm9yIHRoZSBWaXJ1cyBEZWZpbml0aW9ucyBidWNrZXQsIHlvdSBjYW4gc3BlY2lmeSBhbiBleGlzdGluZyBidWNrZXQgYW5kIHByZWZpeCAoRGVmYXVsdDogQ3JlYXRlcyBhIG5ldyBTMyBCdWNrZXQgZm9yIGFjY2VzcyBsb2dzKS5cbiAgICovXG4gIHJlYWRvbmx5IGRlZnNCdWNrZXRBY2Nlc3NMb2dzQ29uZmlnPzogU2VydmVybGVzc0NsYW1zY2FuTG9nZ2luZ1Byb3BzO1xuICAvKipcbiAgICogQWxsb3dzIHRoZSB1c2Ugb2YgaW1wb3J0ZWQgYnVja2V0cy4gV2hlbiB1c2luZyBpbXBvcnRlZCBidWNrZXRzIHRoZSB1c2VyIGlzIHJlc3BvbnNpYmxlIGZvciBhZGRpbmcgdGhlIHJlcXVpcmVkIHBvbGljeSBzdGF0ZW1lbnQgdG8gdGhlIGJ1Y2tldCBwb2xpY3k6IGBnZXRQb2xpY3lTdGF0ZW1lbnRGb3JCdWNrZXQoKWAgY2FuIGJlIHVzZWQgdG8gcmV0cmlldmUgdGhlIHBvbGljeSBzdGF0ZW1lbnQgcmVxdWlyZWQgYnkgdGhlIHNvbHV0aW9uLlxuICAgKi9cbiAgcmVhZG9ubHkgYWNjZXB0UmVzcG9uc2liaWxpdHlGb3JVc2luZ0ltcG9ydGVkQnVja2V0PzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIEFsbG93IGZvciBub24tcm9vdCB1c2VycyB0byBtb2RpZnkvZGVsZXRlIHRoZSBidWNrZXQgcG9saWN5IG9uIHRoZSBWaXJ1cyBEZWZpbml0aW9ucyBidWNrZXQuXG4gICAqIFdhcm5pbmc6IGNoYW5naW5nIHRoaXMgZmxhZyBmcm9tICdmYWxzZScgdG8gJ3RydWUnIG9uIGV4aXN0aW5nIGRlcGxveW1lbnRzIHdpbGwgY2F1c2UgdXBkYXRlcyB0byBmYWlsLlxuICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgKi9cbiAgcmVhZG9ubHkgZGVmc0J1Y2tldEFsbG93UG9saWN5TXV0YXRpb24/OiBib29sZWFuO1xufVxuXG4vKipcbiAgQW4gW2F3cy1jZGtdKGh0dHBzOi8vZ2l0aHViLmNvbS9hd3MvYXdzLWNkaykgY29uc3RydWN0IHRoYXQgdXNlcyBbQ2xhbUFWwq5dKGh0dHBzOi8vd3d3LmNsYW1hdi5uZXQvKS5cbiAgdG8gc2NhbiBvYmplY3RzIGluIEFtYXpvbiBTMyBmb3IgdmlydXNlcy4gVGhlIGNvbnN0cnVjdCBwcm92aWRlcyBhIGZsZXhpYmxlIGludGVyZmFjZSBmb3IgYSBzeXN0ZW1cbiAgdG8gYWN0IGJhc2VkIG9uIHRoZSByZXN1bHRzIG9mIGEgQ2xhbUFWIHZpcnVzIHNjYW4uXG5cbiAgVGhlIGNvbnN0cnVjdCBjcmVhdGVzIGEgTGFtYmRhIGZ1bmN0aW9uIHdpdGggRUZTIGludGVncmF0aW9uIHRvIHN1cHBvcnQgbGFyZ2VyIGZpbGVzLlxuICBBIFZQQyB3aXRoIGlzb2xhdGVkIHN1Ym5ldHMsIGEgUzMgR2F0ZXdheSBlbmRwb2ludCB3aWxsIGFsc28gYmUgY3JlYXRlZC5cblxuICBBZGRpdGlvbmFsbHkgY3JlYXRlcyBhbiB0d2ljZS1kYWlseSBqb2IgdG8gZG93bmxvYWQgdGhlIGxhdGVzdCBDbGFtQVYgZGVmaW5pdGlvbiBmaWxlcyB0byB0aGVcbiAgVmlydXMgRGVmaW5pdGlvbnMgUzMgQnVja2V0IGJ5IHV0aWxpemluZyBhbiBFdmVudEJyaWRnZSBydWxlIGFuZCBhIExhbWJkYSBmdW5jdGlvbiBhbmRcbiAgcHVibGlzaGVzIENsb3VkV2F0Y2ggTWV0cmljcyB0byB0aGUgJ3NlcnZlcmxlc3MtY2xhbXNjYW4nIG5hbWVzcGFjZS5cblxuICBfX0ltcG9ydGFudCBPJk1fXzpcbiAgV2hlbiBDbGFtQVYgcHVibGlzaGVzIHVwZGF0ZXMgdG8gdGhlIHNjYW5uZXIgeW91IHdpbGwgc2VlIOKAnFlvdXIgQ2xhbUFWIGluc3RhbGxhdGlvbiBpcyBPVVREQVRFROKAnSBpbiB5b3VyIHNjYW4gcmVzdWx0cy5cbiAgV2hpbGUgdGhlIGNvbnN0cnVjdCBjcmVhdGVzIGEgc3lzdGVtIHRvIGtlZXAgdGhlIGRhdGFiYXNlIGRlZmluaXRpb25zIHVwIHRvIGRhdGUsIHlvdSBtdXN0IHVwZGF0ZSB0aGUgc2Nhbm5lciB0b1xuICBkZXRlY3QgYWxsIHRoZSBsYXRlc3QgVmlydXNlcy5cblxuICBVcGRhdGUgdGhlIGRvY2tlciBpbWFnZXMgb2YgdGhlIExhbWJkYSBmdW5jdGlvbnMgd2l0aCB0aGUgbGF0ZXN0IHZlcnNpb24gb2YgQ2xhbUFWIGJ5IHJlLXJ1bm5pbmcgYGNkayBkZXBsb3lgLlxuXG4gIFN1Y2Nlc3NmdWwgU2NhbiBFdmVudCBmb3JtYXRcbiAgYGBganNvblxuICB7XG4gICAgIFwic291cmNlXCI6IFwic2VydmVybGVzcy1jbGFtc2NhblwiLFxuICAgICBcImlucHV0X2J1Y2tldFwiOiA8aW5wdXRfYnVja2V0X25hbWU+LFxuICAgICBcImlucHV0X2tleVwiOiA8b2JqZWN0X2tleT4sXG4gICAgIFwic3RhdHVzXCI6IDxcIkNMRUFOXCJ8XCJJTkZFQ1RFRFwifFwiTi9BXCI+LFxuICAgICBcIm1lc3NhZ2VcIjogPHNjYW5fc3VtbWFyeT4sXG4gICB9XG4gIGBgYFxuXG4gIE5vdGU6IFRoZSBWaXJ1cyBEZWZpbml0aW9ucyBidWNrZXQgcG9saWN5IHdpbGwgbGlrZWx5IGNhdXNlIGEgZGVsZXRpb24gZXJyb3IgaWYgeW91IGNob29zZSB0byBkZWxldGVcbiAgdGhlIHN0YWNrIGFzc29jaWF0ZWQgaW4gdGhlIGNvbnN0cnVjdC4gSG93ZXZlciBzaW5jZSB0aGUgYnVja2V0IGl0c2VsZiBnZXRzIGRlbGV0ZWQsIHlvdSBjYW4gZGVsZXRlXG4gIHRoZSBzdGFjayBhZ2FpbiB0byByZXNvbHZlIHRoZSBlcnJvci5cbiAqL1xuZXhwb3J0IGNsYXNzIFNlcnZlcmxlc3NDbGFtc2NhbiBleHRlbmRzIENvbnN0cnVjdCB7XG4gIC8qKlxuICAgIFRoZSBMYW1iZGEgRGVzdGluYXRpb24gZm9yIGZhaWxlZCBvbiBlcnJlZCBzY2FucyBbRVJST1IsIElOIFBST0dSRVNTIChJZiBlcnJvciBpcyBkdWUgdG8gTGFtYmRhIHRpbWVvdXQpXS5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBlcnJvckRlc3Q6IElEZXN0aW5hdGlvbjtcblxuICAvKipcbiAgICBUaGUgTGFtYmRhIERlc3RpbmF0aW9uIGZvciBjb21wbGV0ZWQgQ2xhbUFWIHNjYW5zIFtDTEVBTiwgSU5GRUNURURdLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHJlc3VsdERlc3Q6IElEZXN0aW5hdGlvbjtcblxuICAvKipcbiAgICBDb25kaXRpb25hbDogVGhlIFNRUyBRdWV1ZSBmb3IgZXJyZWQgc2NhbnMgaWYgYSBmYWlsdXJlIChvbkVycm9yKSBkZXN0aW5hdGlvbiB3YXMgbm90IHNwZWNpZmllZC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBlcnJvclF1ZXVlPzogUXVldWU7XG5cbiAgLyoqXG4gICAgQ29uZGl0aW9uYWw6IFRoZSBTUVMgRGVhZCBMZXR0ZXIgUXVldWUgZm9yIHRoZSBlcnJvclF1ZXVlIGlmIGEgZmFpbHVyZSAob25FcnJvcikgZGVzdGluYXRpb24gd2FzIG5vdCBzcGVjaWZpZWQuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZXJyb3JEZWFkTGV0dGVyUXVldWU/OiBRdWV1ZTtcblxuICAvKipcbiAgICBDb25kaXRpb25hbDogVGhlIEV2ZW50IEJyaWRnZSBCdXMgZm9yIGNvbXBsZXRlZCBDbGFtQVYgc2NhbnMgaWYgYSBzdWNjZXNzIChvblJlc3VsdCkgZGVzdGluYXRpb24gd2FzIG5vdCBzcGVjaWZpZWQuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcmVzdWx0QnVzPzogRXZlbnRCdXM7XG5cbiAgLyoqXG4gICAgQ29uZGl0aW9uYWw6IEFuIEV2ZW50IEJyaWRnZSBSdWxlIGZvciBmaWxlcyB0aGF0IGFyZSBtYXJrZWQgJ0NMRUFOJyBieSBDbGFtQVYgaWYgYSBzdWNjZXNzIGRlc3RpbmF0aW9uIHdhcyBub3Qgc3BlY2lmaWVkLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGNsZWFuUnVsZT86IFJ1bGU7XG5cbiAgLyoqXG4gICAgQ29uZGl0aW9uYWw6IEFuIEV2ZW50IEJyaWRnZSBSdWxlIGZvciBmaWxlcyB0aGF0IGFyZSBtYXJrZWQgJ0lORkVDVEVEJyBieSBDbGFtQVYgaWYgYSBzdWNjZXNzIGRlc3RpbmF0aW9uIHdhcyBub3Qgc3BlY2lmaWVkLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGluZmVjdGVkUnVsZT86IFJ1bGU7XG5cbiAgLyoqXG4gICAgQ29uZGl0aW9uYWw6IFRoZSBCdWNrZXQgZm9yIGFjY2VzcyBsb2dzIGZvciB0aGUgdmlydXMgZGVmaW5pdGlvbnMgYnVja2V0IGlmIGxvZ2dpbmcgaXMgZW5hYmxlZCAoZGVmc0J1Y2tldEFjY2Vzc0xvZ3NDb25maWcpLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGRlZnNBY2Nlc3NMb2dzQnVja2V0PzogSUJ1Y2tldDtcblxuICAvKipcbiAgICBDb25kaXRpb25hbDogV2hlbiB0cnVlLCB0aGUgdXNlciBhY2NlcHRlZCB0aGUgcmVzcG9uc2liaWxpdHkgZm9yIHVzaW5nIGltcG9ydGVkIGJ1Y2tldHMuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgdXNlSW1wb3J0ZWRCdWNrZXRzPzogYm9vbGVhbjtcblxuICBwcml2YXRlIF9zY2FuRnVuY3Rpb246IERvY2tlckltYWdlRnVuY3Rpb247XG4gIHByaXZhdGUgX3MzR3c6IEdhdGV3YXlWcGNFbmRwb2ludDtcbiAgcHJpdmF0ZSBfZWZzUm9vdFBhdGggPSAnL2xhbWJkYSc7XG4gIHByaXZhdGUgX2Vmc01vdW50UGF0aCA9IGAvbW50JHt0aGlzLl9lZnNSb290UGF0aH1gO1xuICBwcml2YXRlIF9lZnNEZWZzUGF0aCA9ICd2aXJ1c19kYXRhYmFzZS8nO1xuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgU2VydmVybGVzc0NsYW1zY2FuIGNvbnN0cnVjdC5cbiAgICogQHBhcmFtIHNjb3BlIFRoZSBwYXJlbnQgY3JlYXRpbmcgY29uc3RydWN0ICh1c3VhbGx5IGB0aGlzYCkuXG4gICAqIEBwYXJhbSBpZCBUaGUgY29uc3RydWN0J3MgbmFtZS5cbiAgICogQHBhcmFtIHByb3BzIEEgYFNlcnZlcmxlc3NDbGFtc2NhblByb3BzYCBpbnRlcmZhY2UuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogU2VydmVybGVzc0NsYW1zY2FuUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgdGhpcy51c2VJbXBvcnRlZEJ1Y2tldHMgPSBwcm9wcy5hY2NlcHRSZXNwb25zaWJpbGl0eUZvclVzaW5nSW1wb3J0ZWRCdWNrZXQ7XG5cbiAgICBpZiAoIXByb3BzLm9uUmVzdWx0KSB7XG4gICAgICB0aGlzLnJlc3VsdEJ1cyA9IG5ldyBFdmVudEJ1cyh0aGlzLCAnU2NhblJlc3VsdEJ1cycpO1xuICAgICAgdGhpcy5yZXN1bHREZXN0ID0gbmV3IEV2ZW50QnJpZGdlRGVzdGluYXRpb24odGhpcy5yZXN1bHRCdXMpO1xuICAgICAgdGhpcy5pbmZlY3RlZFJ1bGUgPSBuZXcgUnVsZSh0aGlzLCAnSW5mZWN0ZWRSdWxlJywge1xuICAgICAgICBldmVudEJ1czogdGhpcy5yZXN1bHRCdXMsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRXZlbnQgZm9yIHdoZW4gYSBmaWxlIGlzIG1hcmtlZCBJTkZFQ1RFRCcsXG4gICAgICAgIGV2ZW50UGF0dGVybjoge1xuICAgICAgICAgIGRldGFpbDoge1xuICAgICAgICAgICAgcmVzcG9uc2VQYXlsb2FkOiB7XG4gICAgICAgICAgICAgIHNvdXJjZTogWydzZXJ2ZXJsZXNzLWNsYW1zY2FuJ10sXG4gICAgICAgICAgICAgIHN0YXR1czogWydJTkZFQ1RFRCddLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgICB0aGlzLmNsZWFuUnVsZSA9IG5ldyBSdWxlKHRoaXMsICdDbGVhblJ1bGUnLCB7XG4gICAgICAgIGV2ZW50QnVzOiB0aGlzLnJlc3VsdEJ1cyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdFdmVudCBmb3Igd2hlbiBhIGZpbGUgaXMgbWFya2VkIENMRUFOJyxcbiAgICAgICAgZXZlbnRQYXR0ZXJuOiB7XG4gICAgICAgICAgZGV0YWlsOiB7XG4gICAgICAgICAgICByZXNwb25zZVBheWxvYWQ6IHtcbiAgICAgICAgICAgICAgc291cmNlOiBbJ3NlcnZlcmxlc3MtY2xhbXNjYW4nXSxcbiAgICAgICAgICAgICAgc3RhdHVzOiBbJ0NMRUFOJ10sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZXN1bHREZXN0ID0gcHJvcHMub25SZXN1bHQ7XG4gICAgfVxuXG4gICAgaWYgKCFwcm9wcy5vbkVycm9yKSB7XG4gICAgICB0aGlzLmVycm9yRGVhZExldHRlclF1ZXVlID0gbmV3IFF1ZXVlKHRoaXMsICdTY2FuRXJyb3JEZWFkTGV0dGVyUXVldWUnLCB7XG4gICAgICAgIGVuY3J5cHRpb246IFF1ZXVlRW5jcnlwdGlvbi5LTVNfTUFOQUdFRCxcbiAgICAgIH0pO1xuICAgICAgdGhpcy5lcnJvckRlYWRMZXR0ZXJRdWV1ZS5hZGRUb1Jlc291cmNlUG9saWN5KG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBhY3Rpb25zOiBbJ3NxczoqJ10sXG4gICAgICAgIGVmZmVjdDogRWZmZWN0LkRFTlksXG4gICAgICAgIHByaW5jaXBhbHM6IFtuZXcgQW55UHJpbmNpcGFsKCldLFxuICAgICAgICBjb25kaXRpb25zOiB7IEJvb2w6IHsgJ2F3czpTZWN1cmVUcmFuc3BvcnQnOiBmYWxzZSB9IH0sXG4gICAgICAgIHJlc291cmNlczogW3RoaXMuZXJyb3JEZWFkTGV0dGVyUXVldWUucXVldWVBcm5dLFxuICAgICAgfSkpO1xuICAgICAgdGhpcy5lcnJvclF1ZXVlID0gbmV3IFF1ZXVlKHRoaXMsICdTY2FuRXJyb3JRdWV1ZScsIHtcbiAgICAgICAgZW5jcnlwdGlvbjogUXVldWVFbmNyeXB0aW9uLktNU19NQU5BR0VELFxuICAgICAgICBkZWFkTGV0dGVyUXVldWU6IHtcbiAgICAgICAgICBtYXhSZWNlaXZlQ291bnQ6IDMsXG4gICAgICAgICAgcXVldWU6IHRoaXMuZXJyb3JEZWFkTGV0dGVyUXVldWUsXG4gICAgICAgIH0sXG4gICAgICB9KTtcbiAgICAgIHRoaXMuZXJyb3JRdWV1ZS5hZGRUb1Jlc291cmNlUG9saWN5KG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBhY3Rpb25zOiBbJ3NxczoqJ10sXG4gICAgICAgIGVmZmVjdDogRWZmZWN0LkRFTlksXG4gICAgICAgIHByaW5jaXBhbHM6IFtuZXcgQW55UHJpbmNpcGFsKCldLFxuICAgICAgICBjb25kaXRpb25zOiB7IEJvb2w6IHsgJ2F3czpTZWN1cmVUcmFuc3BvcnQnOiBmYWxzZSB9IH0sXG4gICAgICAgIHJlc291cmNlczogW3RoaXMuZXJyb3JRdWV1ZS5xdWV1ZUFybl0sXG4gICAgICB9KSk7XG4gICAgICB0aGlzLmVycm9yRGVzdCA9IG5ldyBTcXNEZXN0aW5hdGlvbih0aGlzLmVycm9yUXVldWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmVycm9yRGVzdCA9IHByb3BzLm9uRXJyb3I7XG4gICAgfVxuXG4gICAgY29uc3QgdnBjID0gbmV3IFZwYyh0aGlzLCAnU2NhblZQQycsIHtcbiAgICAgIHN1Ym5ldENvbmZpZ3VyYXRpb246IFtcbiAgICAgICAge1xuICAgICAgICAgIHN1Ym5ldFR5cGU6IFN1Ym5ldFR5cGUuUFJJVkFURV9JU09MQVRFRCxcbiAgICAgICAgICBuYW1lOiAnSXNvbGF0ZWQnLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcblxuICAgIHZwYy5hZGRGbG93TG9nKCdGbG93TG9ncycpO1xuXG4gICAgdGhpcy5fczNHdyA9IHZwYy5hZGRHYXRld2F5RW5kcG9pbnQoJ1MzRW5kcG9pbnQnLCB7XG4gICAgICBzZXJ2aWNlOiBHYXRld2F5VnBjRW5kcG9pbnRBd3NTZXJ2aWNlLlMzLFxuICAgIH0pO1xuXG4gICAgY29uc3QgZmlsZVN5c3RlbSA9IG5ldyBGaWxlU3lzdGVtKHRoaXMsICdTY2FuRmlsZVN5c3RlbScsIHtcbiAgICAgIHZwYzogdnBjLFxuICAgICAgZW5jcnlwdGVkOiBwcm9wcy5lZnNFbmNyeXB0aW9uID09PSBmYWxzZSA/IGZhbHNlIDogdHJ1ZSxcbiAgICAgIGxpZmVjeWNsZVBvbGljeTogTGlmZWN5Y2xlUG9saWN5LkFGVEVSXzdfREFZUyxcbiAgICAgIHBlcmZvcm1hbmNlTW9kZTogcHJvcHMuZWZzUGVyZm9ybWFuY2VNb2RlID8/IFBlcmZvcm1hbmNlTW9kZS5HRU5FUkFMX1BVUlBPU0UsXG4gICAgICB0aHJv