UNPKG

cdk-serverless-clamscan

Version:

Serverless architecture to virus scan objects in Amazon S3.

409 lines (403 loc) 65 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: '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