UNPKG

@k9securityio/k9-cdk

Version:

Provision strong AWS security policies easily using the AWS CDK.

188 lines 30 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.grantAccessViaResourcePolicy = exports.CloudFrontOACReadAccessGenerator = exports.SID_ALLOW_PUBLIC_READ_ACCESS = exports.SID_DENY_UNENCRYPTED_STORAGE = exports.SID_DENY_UNEXPECTED_ENCRYPTION_METHOD = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const aws_iam_1 = require("aws-cdk-lib/aws-iam"); const s3 = require("aws-cdk-lib/aws-s3"); const aws_s3_1 = require("aws-cdk-lib/aws-s3"); const aws_iam_utils = require("./aws-iam-utils"); const k9policy_1 = require("./k9policy"); let SUPPORTED_CAPABILITIES = new Array(k9policy_1.AccessCapability.ADMINISTER_RESOURCE, k9policy_1.AccessCapability.READ_CONFIG, k9policy_1.AccessCapability.READ_DATA, k9policy_1.AccessCapability.WRITE_DATA, k9policy_1.AccessCapability.DELETE_DATA); exports.SID_DENY_UNEXPECTED_ENCRYPTION_METHOD = 'DenyUnexpectedEncryptionMethod'; exports.SID_DENY_UNENCRYPTED_STORAGE = 'DenyUnencryptedStorage'; exports.SID_ALLOW_PUBLIC_READ_ACCESS = 'AllowPublicReadAccess'; class CloudFrontOACReadAccessGenerator { constructor(bucket, distributionArn) { this.bucket = bucket; this.distributionArn = distributionArn; } makeAllowStatements() { return [new aws_iam_1.PolicyStatement({ sid: CloudFrontOACReadAccessGenerator.SID_ALLOW_CLOUDFRONT_OAC_READ_ACCESS, effect: aws_iam_1.Effect.ALLOW, principals: [new aws_iam_1.ServicePrincipal('cloudfront.amazonaws.com')], actions: ['s3:GetObject'], resources: [`${this.bucket.arnForObjects('*')}`], conditions: { StringEquals: { 'aws:SourceArn': this.distributionArn }, }, })]; } makeConditionsToExceptFromDenyEveryoneElse() { // return a (TypeScript) Record of the form: // {"Operator": { "keyInRequestContext": "value" } } return { StringNotEqualsIfExists: { 'aws:PrincipalServiceName': 'cloudfront.amazonaws.com' } }; } } exports.CloudFrontOACReadAccessGenerator = CloudFrontOACReadAccessGenerator; _a = JSII_RTTI_SYMBOL_1; CloudFrontOACReadAccessGenerator[_a] = { fqn: "@k9securityio/k9-cdk.s3.CloudFrontOACReadAccessGenerator", version: "2.1.3" }; CloudFrontOACReadAccessGenerator.SID_ALLOW_CLOUDFRONT_OAC_READ_ACCESS = 'AllowCloudFrontOACReadAccess'; /** * Grants least-privilege access to a bucket by generating a BucketPolicy from the access capabilities * described by `props`; the policy will be set on the Bucket specified in `props`. * * When a BucketPolicy already exists on the Bucket referenced in `props`: * * the BucketPolicy's existing Statements will pass through unmodified * * k9 will identify IAM principals there were allowed by the original policy and add those principals to * the `DenyEveryoneElse` Statement's exclusion list so that, e.g. autoDeleteObjects works as expected * * k9's Allow and Deny statements will be added to the policy * * @remarks * * k9 modifies the existing BucketPolicy in place instead of replacing or copying and modifying that * to preserve dependency references created by certain S3 CDK features such as `autoDeleteObjects`. * * @param scope The scope in which to define this construct. * @param id The scoped construct ID. * @param props describing the desired access capabilities for the bucket * * @return an array of AddToResourcePolicyResult */ function grantAccessViaResourcePolicy(scope, id, props) { const policyFactory = new k9policy_1.K9PolicyFactory(); // If the bucket already has a policy, use it. Maintaining the existing policy instance // is important because other CDK features like S3 autoDeleteObjects may have expressed dependencies // on that instance which must be maintained. if (!props.bucket.policy) { props.bucket.policy = new s3.BucketPolicy(scope, `${id}Policy`, { bucket: props.bucket }); } const policy = props.bucket.policy; const addToResourcePolicyResults = new Array(); let resourceArns = [ `${props.bucket.bucketArn}`, `${props.bucket.arnForObjects('*')}`, ]; // Capture the principals that were allowed prior to modifying policy // One could argue this can be done at the end because we're going to // narrow the DenyEveryoneElse to the unique set of allowed principals. // Record here for now to preserve ability to generate fine-grained DenyEveryoneElse-$capability statements. const origAllowedAWSPrincipals = aws_iam_utils.getAllowedPrincipalArns(policy.document); // Make Allow Statements const k9Statements = policyFactory.makeAllowStatements('S3', SUPPORTED_CAPABILITIES, props.k9DesiredAccess, resourceArns); if (props.publicReadAccess) { k9Statements.unshift(// very important statement; put at beginning. new aws_iam_1.PolicyStatement({ sid: exports.SID_ALLOW_PUBLIC_READ_ACCESS, effect: aws_iam_1.Effect.ALLOW, principals: [new aws_iam_1.AnyPrincipal()], actions: ['s3:GetObject'], resources: [`${props.bucket.arnForObjects('*')}`], })); } if (props.awsServiceAccessGenerators) { for (let serviceAccessSpec of props.awsServiceAccessGenerators) { let allowStatements = serviceAccessSpec.makeAllowStatements(); k9Statements.unshift(...allowStatements); } } // Make Deny Statement const denyEveryoneElseTest = policyFactory.wasLikeUsed(props.k9DesiredAccess) ? 'ArnNotLike' : 'ArnNotEquals'; let denyEveryoneElseStatement; if (props.publicReadAccess) { denyEveryoneElseStatement = new aws_iam_1.PolicyStatement({ sid: 'DenyEveryoneElse', effect: aws_iam_1.Effect.DENY, principals: policyFactory.makeDenyEveryoneElsePrincipals(), notActions: ['s3:GetObject'], resources: resourceArns, }); } else { denyEveryoneElseStatement = new aws_iam_1.PolicyStatement({ sid: 'DenyEveryoneElse', effect: aws_iam_1.Effect.DENY, principals: policyFactory.makeDenyEveryoneElsePrincipals(), actions: ['s3:*'], resources: resourceArns, }); } const allAllowedPrincipalArns = new Set(policyFactory.getAllowedPrincipalArns(props.k9DesiredAccess)); for (let origAWSPrincipal of origAllowedAWSPrincipals) { allAllowedPrincipalArns.add(origAWSPrincipal); } denyEveryoneElseStatement.addCondition(denyEveryoneElseTest, { 'aws:PrincipalArn': [...allAllowedPrincipalArns] }); if (props.awsServiceAccessGenerators) { for (let serviceAccessSpec of props.awsServiceAccessGenerators) { let conditionsToExceptFromDenyEveryoneElse = serviceAccessSpec.makeConditionsToExceptFromDenyEveryoneElse(); let conditionOps = Object.keys(conditionsToExceptFromDenyEveryoneElse); for (let conditionOp of conditionOps) { // note: when you call PolicyStatement#addCondition with the same conditionOp (e.g. StringEquals) // multiple times, addCondition will collect the values into an array. // c.f. https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-iam/lib/policy-statement.ts#L364 denyEveryoneElseStatement.addCondition(conditionOp, conditionsToExceptFromDenyEveryoneElse[conditionOp]); } } } // default encryption method to SSE-KMS, // allow override to SSE-S3 (AES256) let encryptionMethod = 'aws:kms'; if (props.encryption) { if (aws_s3_1.BucketEncryption.S3_MANAGED == props.encryption) { encryptionMethod = 'AES256'; } } k9Statements.push(new aws_iam_1.PolicyStatement({ sid: 'DenyInsecureCommunications', effect: aws_iam_1.Effect.DENY, principals: [new aws_iam_1.AnyPrincipal()], actions: ['s3:*'], resources: resourceArns, conditions: { Bool: { 'aws:SecureTransport': false }, }, })); if (props.enforceEncryptionAtRest ?? true) { k9Statements.push(new aws_iam_1.PolicyStatement({ sid: exports.SID_DENY_UNENCRYPTED_STORAGE, effect: aws_iam_1.Effect.DENY, principals: [new aws_iam_1.AnyPrincipal()], actions: ['s3:PutObject', 's3:ReplicateObject'], resources: resourceArns, conditions: { Null: { 's3:x-amz-server-side-encryption': true }, }, }), new aws_iam_1.PolicyStatement({ sid: exports.SID_DENY_UNEXPECTED_ENCRYPTION_METHOD, effect: aws_iam_1.Effect.DENY, principals: [new aws_iam_1.AnyPrincipal()], actions: ['s3:PutObject', 's3:ReplicateObject'], resources: resourceArns, conditions: { StringNotEquals: { 's3:x-amz-server-side-encryption': encryptionMethod }, }, })); } k9Statements.push(denyEveryoneElseStatement); for (let statement of k9Statements) { let addToResourcePolicyResult = props.bucket.addToResourcePolicy(statement); addToResourcePolicyResults.push(addToResourcePolicyResult); } policy.document.validateForResourcePolicy(); return addToResourcePolicyResults; } exports.grantAccessViaResourcePolicy = grantAccessViaResourcePolicy; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiczMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvczMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxpREFPNkI7QUFDN0IseUNBQXlDO0FBQ3pDLCtDQUErRDtBQUUvRCxpREFBaUQ7QUFDakQseUNBQXdHO0FBMkN4RyxJQUFJLHNCQUFzQixHQUFHLElBQUksS0FBSyxDQUNwQywyQkFBZ0IsQ0FBQyxtQkFBbUIsRUFDcEMsMkJBQWdCLENBQUMsV0FBVyxFQUM1QiwyQkFBZ0IsQ0FBQyxTQUFTLEVBQzFCLDJCQUFnQixDQUFDLFVBQVUsRUFDM0IsMkJBQWdCLENBQUMsV0FBVyxDQUM3QixDQUFDO0FBRVcsUUFBQSxxQ0FBcUMsR0FBRyxnQ0FBZ0MsQ0FBQztBQUN6RSxRQUFBLDRCQUE0QixHQUFHLHdCQUF3QixDQUFDO0FBQ3hELFFBQUEsNEJBQTRCLEdBQUcsdUJBQXVCLENBQUM7QUFFcEUsTUFBYSxnQ0FBZ0M7SUFPM0MsWUFBWSxNQUFlLEVBQUUsZUFBdUI7UUFDbEQsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLGVBQWUsR0FBRyxlQUFlLENBQUM7SUFDekMsQ0FBQztJQUVELG1CQUFtQjtRQUNqQixPQUFPLENBQUMsSUFBSSx5QkFBZSxDQUFDO2dCQUMxQixHQUFHLEVBQUUsZ0NBQWdDLENBQUMsb0NBQW9DO2dCQUMxRSxNQUFNLEVBQUUsZ0JBQU0sQ0FBQyxLQUFLO2dCQUNwQixVQUFVLEVBQUUsQ0FBQyxJQUFJLDBCQUFnQixDQUFDLDBCQUEwQixDQUFDLENBQUM7Z0JBQzlELE9BQU8sRUFBRSxDQUFDLGNBQWMsQ0FBQztnQkFDekIsU0FBUyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUNoRCxVQUFVLEVBQUU7b0JBQ1YsWUFBWSxFQUFFLEVBQUUsZUFBZSxFQUFFLElBQUksQ0FBQyxlQUFlLEVB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QUFpQyxFQUFFLElBQUksRUFBRTthQUNsRDtTQUNGLENBQ0EsRUFDRCxJQUFJLHlCQUFlLENBQUM7WUFDbEIsR0FBRyxFQUFFLDZDQUFxQztZQUMxQyxNQUFNLEVBQUUsZ0JBQU0sQ0FBQyxJQUFJO1lBQ25CLFVBQVUsRUFBRSxDQUFDLElBQUksc0JBQVksRUFBRSxDQUFDO1lBQ2hDLE9BQU8sRUFBRSxDQUFDLGNBQWMsRUFBRSxvQkFBb0IsQ0FBQztZQUMvQyxTQUFTLEVBQUUsWUFBWTtZQUN2QixVQUFVLEVBQUU7Z0JBQ1YsZUFBZSxFQUFFLEVBQUUsaUNBQWlDLEVBQUUsZ0JBQWdCLEVBQUU7YUFDekU7U0FDRixDQUNBLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRCxZQUFZLENBQUMsSUFBSSxDQUFDLHlCQUF5QixDQUFDLENBQUM7SUFFN0MsS0FBSyxJQUFJLFNBQVMsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUNuQyxJQUFJLHlCQUF5QixHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsbUJBQW1CLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDNUUsMEJBQTBCLENBQUMsSUFBSSxDQUFDLHlCQUF5QixDQUFDLENBQUM7SUFDN0QsQ0FBQztJQUVELE1BQU0sQ0FBQyxRQUFRLENBQUMseUJBQXlCLEVBQUUsQ0FBQztJQUU1QyxPQUFPLDBCQUEwQixDQUFDO0FBQ3BDLENBQUM7QUFuSkQsb0VBbUpDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWRkVG9SZXNvdXJjZVBvbGljeVJlc3VsdCxcbiAgQW55UHJpbmNpcGFsLFxuICBDb25kaXRpb25zLFxuICBFZmZlY3QsXG4gIFBvbGljeVN0YXRlbWVudCxcbiAgU2VydmljZVByaW5jaXBhbCxcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWlhbSc7XG5pbXBvcnQgKiBhcyBzMyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtczMnO1xuaW1wb3J0IHsgSUJ1Y2tldCwgQnVja2V0RW5jcnlwdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMyc7XG5pbXBvcnQgeyBJQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgKiBhcyBhd3NfaWFtX3V0aWxzIGZyb20gJy4vYXdzLWlhbS11dGlscyc7XG5pbXBvcnQgeyBBY2Nlc3NDYXBhYmlsaXR5LCBJQWNjZXNzU3BlYywgSUFXU1NlcnZpY2VBY2Nlc3NHZW5lcmF0b3IsIEs5UG9saWN5RmFjdG9yeSB9IGZyb20gJy4vazlwb2xpY3knO1xuXG4vKipcbiAqIENvbmZpZ3VyZSB0aGUgazkgU2VjdXJpdHkgUzMgQnVja2V0IHBvbGljeSBnZW5lcmF0b3Igd2l0aCB0aGUgSzlCdWNrZXRQb2xpY3lQcm9wcy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBLOUJ1Y2tldFBvbGljeVByb3BzIGV4dGVuZHMgczMuQnVja2V0UG9saWN5UHJvcHMge1xuICAvKipcbiAgICogQW4gYXJyYXkgb2YgSUFjY2Vzc1NwZWMgZGVmaW5pbmcgdGhlIGRlc2lyZWQgYWNjZXNzLiAgVGhlIHBvbGljeVxuICAgKiBnZW5lcmF0b3Igd2lsbCBjb21iaW5lIGFuZCBub3JtYWxpemUgb3ZlcmxhcHBpbmcgYWNjZXNzIHNwZWNzLlxuICAgKi9cbiAgcmVhZG9ubHkgazlEZXNpcmVkQWNjZXNzOiBBcnJheTxJQWNjZXNzU3BlYz47XG5cbiAgLyoqXG4gICAqIChPcHRpb25hbGx5KSBQcm92aWRlIHRoZSBCdWNrZXRFbmNyeXB0aW9uIG9iamVjdCBmb3IgdGhlIEJ1Y2tldCB0b1xuICAgKiBhbGxvdyB0aGUgcG9saWN5IGdlbmVyYXRvciB0byBjdXN0b21pemUgdGhlIHBvbGljeSBmb3IgdGhlIEJ1Y2tldCdzXG4gICAqIGNvbmZpZ3VyYXRpb24gd2l0aG91dCBoYW5kbGluZywgZS5nLiB0aGUgZW5jcnlwdGlvbiBtZXRob2Qgb3B0aW9ucyBkaXJlY3RseVxuICAgKi9cbiAgcmVhZG9ubHkgZW5jcnlwdGlvbj86IEJ1Y2tldEVuY3J5cHRpb247XG5cbiAgLyoqXG4gICAqIEVuZm9yY2UgZW5jcnlwdGlvbiBhdCByZXN0IHdpdGggcG9saWN5IGNvbmRpdGlvbnMuICBUaGUgcG9saWN5IHdpbGwgdXNlXG4gICAqIHRoZSBlbmNyeXB0aW9uIG1ldGhvZCBkZWZpbmVkIGJ5IHRoZSBlbmNyeXB0aW9uIHByb3BlcnR5IG9yIGRlZmF1bHQgdG8gYGF3czprbXNgLlxuICAgKlxuICAgKiBAZGVmYXVsdCB0cnVlXG4gICAqL1xuICByZWFkb25seSBlbmZvcmNlRW5jcnlwdGlvbkF0UmVzdD86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIEFsbG93IHB1YmxpYyByZWFkIGFjY2VzcyB0byB0aGUgYnVja2V0LlxuICAgKlxuICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgKi9cbiAgcmVhZG9ubHkgcHVibGljUmVhZEFjY2Vzcz86IGJvb2xlYW47XG5cbiAgLyoqXG4gICAqIEFuIChvcHRpb25hbCkgYXJyYXkgb2YgSUFXU1NlcnZpY2VBY2Nlc3NHZW5lcmF0b3IgaW5zdGFuY2VzIHdoaWNoIHdpbGwgZ2VuZXJhdGUgc3RhdGVtZW50cyB0byBhbGxvdyBhY2Nlc3MgdG8gdGhlXG4gICAqIGJ1Y2tldCBvciBidWNrZXQgb2JqZWN0KHMpIGJ5IGFuIEFXUyBzZXJ2aWNlIGxpa2UgQ2xvdWRGcm9udCBvciBLaW5lc2lzLlxuICAgKlxuICAgKiBAZGVmYXVsdCB1bmRlZmluZWRcbiAgICovXG4gIHJlYWRvbmx5IGF3c1NlcnZpY2VBY2Nlc3NHZW5lcmF0b3JzPzogQXJyYXk8SUFXU1NlcnZpY2VBY2Nlc3NHZW5lcmF0b3I+O1xufVxuXG5sZXQgU1VQUE9SVEVEX0NBUEFCSUxJVElFUyA9IG5ldyBBcnJheTxBY2Nlc3NDYXBhYmlsaXR5PihcbiAgQWNjZXNzQ2FwYWJpbGl0eS5BRE1JTklTVEVSX1JFU09VUkNFLFxuICBBY2Nlc3NDYXBhYmlsaXR5LlJFQURfQ09ORklHLFxuICBBY2Nlc3NDYXBhYmlsaXR5LlJFQURfREFUQSxcbiAgQWNjZXNzQ2FwYWJpbGl0eS5XUklURV9EQVRBLFxuICBBY2Nlc3NDYXBhYmlsaXR5LkRFTEVURV9EQVRBLFxuKTtcblxuZXhwb3J0IGNvbnN0IFNJRF9ERU5ZX1VORVhQRUNURURfRU5DUllQVElPTl9NRVRIT0QgPSAnRGVueVVuZXhwZWN0ZWRFbmNyeXB0aW9uTWV0aG9kJztcbmV4cG9ydCBjb25zdCBTSURfREVOWV9VTkVOQ1JZUFRFRF9TVE9SQUdFID0gJ0RlbnlVbmVuY3J5cHRlZFN0b3JhZ2UnO1xuZXhwb3J0IGNvbnN0IFNJRF9BTExPV19QVUJMSUNfUkVBRF9BQ0NFU1MgPSAnQWxsb3dQdWJsaWNSZWFkQWNjZXNzJztcblxuZXhwb3J0IGNsYXNzIENsb3VkRnJvbnRPQUNSZWFkQWNjZXNzR2VuZXJhdG9yIGltcGxlbWVudHMgSUFXU1NlcnZpY2VBY2Nlc3NHZW5lcmF0b3Ige1xuXG4gIHN0YXRpYyByZWFkb25seSBTSURfQUxMT1dfQ0xPVURGUk9OVF9PQUNfUkVBRF9BQ0NFU1MgPSAnQWxsb3dDbG91ZEZyb250T0FDUmVhZEFjY2Vzcyc7XG5cbiAgcmVhZG9ubHkgYnVja2V0OiBJQnVja2V0O1xuICByZWFkb25seSBkaXN0cmlidXRpb25Bcm46IHN0cmluZztcblxuICBjb25zdHJ1Y3RvcihidWNrZXQ6IElCdWNrZXQsIGRpc3RyaWJ1dGlvbkFybjogc3RyaW5nKSB7XG4gICAgdGhpcy5idWNrZXQgPSBidWNrZXQ7XG4gICAgdGhpcy5kaXN0cmlidXRpb25Bcm4gPSBkaXN0cmlidXRpb25Bcm47XG4gIH1cblxuICBtYWtlQWxsb3dTdGF0ZW1lbnRzKCk6IEFycmF5PFBvbGljeVN0YXRlbWVudD4ge1xuICAgIHJldHVybiBbbmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICBzaWQ6IENsb3VkRnJvbnRPQUNSZWFkQWNjZXNzR2VuZXJhdG9yLlNJRF9BTExPV19DTE9VREZST05UX09BQ19SRUFEX0FDQ0VTUyxcbiAgICAgIGVmZmVjdDogRWZmZWN0LkFMTE9XLFxuICAgICAgcHJpbmNpcGFsczogW25ldyBTZXJ2aWNlUHJpbmNpcGFsKCdjbG91ZGZyb250LmFtYXpvbmF3cy5jb20nKV0sXG4gICAgICBhY3Rpb25zOiBbJ3MzOkdldE9iamVjdCddLFxuICAgICAgcmVzb3VyY2VzOiBbYCR7dGhpcy5idWNrZXQuYXJuRm9yT2JqZWN0cygnKicpfWBdLFxuICAgICAgY29uZGl0aW9uczoge1xuICAgICAgICBTdHJpbmdFcXVhbHM6IHsgJ2F3czpTb3VyY2VBcm4nOiB0aGlzLmRpc3RyaWJ1dGlvbkFybiB9LFxuICAgICAgfSxcbiAgICB9KV07XG4gIH1cblxuICBtYWtlQ29uZGl0aW9uc1RvRXhjZXB0RnJvbURlbnlFdmVyeW9uZUVsc2UoKTogQ29uZGl0aW9ucyB7XG4gICAgLy8gcmV0dXJuIGEgKFR5cGVTY3JpcHQpIFJlY29yZCBvZiB0aGUgZm9ybTpcbiAgICAvLyAgICAge1wiT3BlcmF0b3JcIjogeyBcImtleUluUmVxdWVzdENvbnRleHRcIjogXCJ2YWx1ZVwiIH0gfVxuICAgIHJldHVybiB7IFN0cmluZ05vdEVxdWFsc0lmRXhpc3RzOiB7ICdhd3M6UHJpbmNpcGFsU2VydmljZU5hbWUnOiAnY2xvdWRmcm9udC5hbWF6b25hd3MuY29tJyB9IH07XG4gIH1cbn1cblxuLyoqXG4gKiBHcmFudHMgbGVhc3QtcHJpdmlsZWdlIGFjY2VzcyB0byBhIGJ1Y2tldCBieSBnZW5lcmF0aW5nIGEgQnVja2V0UG9saWN5IGZyb20gdGhlIGFjY2VzcyBjYXBhYmlsaXRpZXNcbiAqIGRlc2NyaWJlZCBieSBgcHJvcHNgOyB0aGUgcG9saWN5IHdpbGwgYmUgc2V0IG9uIHRoZSBCdWNrZXQgc3BlY2lmaWVkIGluIGBwcm9wc2AuXG4gKlxuICogV2hlbiBhIEJ1Y2tldFBvbGljeSBhbHJlYWR5IGV4aXN0cyBvbiB0aGUgQnVja2V0IHJlZmVyZW5jZWQgaW4gYHByb3BzYDpcbiAqICAgKiB0aGUgQnVja2V0UG9saWN5J3MgZXhpc3RpbmcgU3RhdGVtZW50cyB3aWxsIHBhc3MgdGhyb3VnaCB1bm1vZGlmaWVkXG4gKiAgICogazkgd2lsbCBpZGVudGlmeSBJQU0gcHJpbmNpcGFscyB0aGVyZSB3ZXJlIGFsbG93ZWQgYnkgdGhlIG9yaWdpbmFsIHBvbGljeSBhbmQgYWRkIHRob3NlIHByaW5jaXBhbHMgdG9cbiAqICAgdGhlIGBEZW55RXZlcnlvbmVFbHNlYCBTdGF0ZW1lbnQncyBleGNsdXNpb24gbGlzdCBzbyB0aGF0LCBlLmcuIGF1dG9EZWxldGVPYmplY3RzIHdvcmtzIGFzIGV4cGVjdGVkXG4gKiAgICogazkncyBBbGxvdyBhbmQgRGVueSBzdGF0ZW1lbnRzIHdpbGwgYmUgYWRkZWQgdG8gdGhlIHBvbGljeVxuICpcbiAqIEByZW1hcmtzXG4gKlxuICogazkgbW9kaWZpZXMgdGhlIGV4aXN0aW5nIEJ1Y2tldFBvbGljeSBpbiBwbGFjZSBpbnN0ZWFkIG9mIHJlcGxhY2luZyBvciBjb3B5aW5nIGFuZCBtb2RpZnlpbmcgdGhhdFxuICogdG8gcHJlc2VydmUgZGVwZW5kZW5jeSByZWZlcmVuY2VzIGNyZWF0ZWQgYnkgY2VydGFpbiBTMyBDREsgZmVhdHVyZXMgc3VjaCBhcyBgYXV0b0RlbGV0ZU9iamVjdHNgLlxuICpcbiAqIEBwYXJhbSBzY29wZSBUaGUgc2NvcGUgaW4gd2hpY2ggdG8gZGVmaW5lIHRoaXMgY29uc3RydWN0LlxuICogQHBhcmFtIGlkIFRoZSBzY29wZWQgY29uc3RydWN0IElELlxuICogQHBhcmFtIHByb3BzIGRlc2NyaWJpbmcgdGhlIGRlc2lyZWQgYWNjZXNzIGNhcGFiaWxpdGllcyBmb3IgdGhlIGJ1Y2tldFxuICpcbiAqIEByZXR1cm4gYW4gYXJyYXkgb2YgQWRkVG9SZXNvdXJjZVBvbGljeVJlc3VsdFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ3JhbnRBY2Nlc3NWaWFSZXNvdXJjZVBvbGljeShzY29wZTogSUNvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IEs5QnVja2V0UG9saWN5UHJvcHMpOiBBZGRUb1Jlc291cmNlUG9saWN5UmVzdWx0W10ge1xuICBjb25zdCBwb2xpY3lGYWN0b3J5ID0gbmV3IEs5UG9saWN5RmFjdG9yeSgpO1xuICAvLyBJZiB0aGUgYnVja2V0IGFscmVhZHkgaGFzIGEgcG9saWN5LCB1c2UgaXQuICBNYWludGFpbmluZyB0aGUgZXhpc3RpbmcgcG9saWN5IGluc3RhbmNlXG4gIC8vIGlzIGltcG9ydGFudCBiZWNhdXNlIG90aGVyIENESyBmZWF0dXJlcyBsaWtlIFMzIGF1dG9EZWxldGVPYmplY3RzIG1heSBoYXZlIGV4cHJlc3NlZCBkZXBlbmRlbmNpZXNcbiAgLy8gb24gdGhhdCBpbnN0YW5jZSB3aGljaCBtdXN0IGJlIG1haW50YWluZWQuXG4gIGlmICghcHJvcHMuYnVja2V0LnBvbGljeSkge1xuICAgIHByb3BzLmJ1Y2tldC5wb2xpY3kgPSBuZXcgczMuQnVja2V0UG9saWN5KHNjb3BlLCBgJHtpZH1Qb2xpY3lgLCB7IGJ1Y2tldDogcHJvcHMuYnVja2V0IH0pO1xuICB9XG4gIGNvbnN0IHBvbGljeSA9IHByb3BzLmJ1Y2tldC5wb2xpY3k7XG5cbiAgY29uc3QgYWRkVG9SZXNvdXJjZVBvbGljeVJlc3VsdHMgPSBuZXcgQXJyYXk8QWRkVG9SZXNvdXJjZVBvbGljeVJlc3VsdD4oKTtcbiAgbGV0IHJlc291cmNlQXJucyA9IFtcbiAgICBgJHtwcm9wcy5idWNrZXQuYnVja2V0QXJufWAsXG4gICAgYCR7cHJvcHMuYnVja2V0LmFybkZvck9iamVjdHMoJyonKX1gLFxuICBdO1xuXG4gIC8vIENhcHR1cmUgdGhlIHByaW5jaXBhbHMgdGhhdCB3ZXJlIGFsbG93ZWQgcHJpb3IgdG8gbW9kaWZ5aW5nIHBvbGljeVxuICAvLyBPbmUgY291bGQgYXJndWUgdGhpcyBjYW4gYmUgZG9uZSBhdCB0aGUgZW5kIGJlY2F1c2Ugd2UncmUgZ29pbmcgdG9cbiAgLy8gbmFycm93IHRoZSBEZW55RXZlcnlvbmVFbHNlIHRvIHRoZSB1bmlxdWUgc2V0IG9mIGFsbG93ZWQgcHJpbmNpcGFscy5cbiAgLy8gUmVjb3JkIGhlcmUgZm9yIG5vdyB0byBwcmVzZXJ2ZSBhYmlsaXR5IHRvIGdlbmVyYXRlIGZpbmUtZ3JhaW5lZCBEZW55RXZlcnlvbmVFbHNlLSRjYXBhYmlsaXR5IHN0YXRlbWVudHMuXG4gIGNvbnN0IG9yaWdBbGxvd2VkQVdTUHJpbmNpcGFscyA9IGF3c19pYW1fdXRpbHMuZ2V0QWxsb3dlZFByaW5jaXBhbEFybnMocG9saWN5LmRvY3VtZW50KTtcblxuICAvLyBNYWtlIEFsbG93IFN0YXRlbWVudHNcbiAgY29uc3QgazlTdGF0ZW1lbnRzID0gcG9saWN5RmFjdG9yeS5tYWtlQWxsb3dTdGF0ZW1lbnRzKCdTMycsXG4gICAgU1VQUE9SVEVEX0NBUEFCSUxJVElFUyxcbiAgICBwcm9wcy5rOURlc2lyZWRBY2Nlc3MsXG4gICAgcmVzb3VyY2VBcm5zKTtcblxuICBpZiAocHJvcHMucHVibGljUmVhZEFjY2Vzcykge1xuICAgIGs5U3RhdGVtZW50cy51bnNoaWZ0KCAvLyB2ZXJ5IGltcG9ydGFudCBzdGF0ZW1lbnQ7IHB1dCBhdCBiZWdpbm5pbmcuXG4gICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgc2lkOiBTSURfQUxMT1dfUFVCTElDX1JFQURfQUNDRVNTLFxuICAgICAgICBlZmZlY3Q6IEVmZmVjdC5BTExPVyxcbiAgICAgICAgcHJpbmNpcGFsczogW25ldyBBbnlQcmluY2lwYWwoKV0sXG4gICAgICAgIGFjdGlvbnM6IFsnczM6R2V0T2JqZWN0J10sXG4gICAgICAgIHJlc291cmNlczogW2Ake3Byb3BzLmJ1Y2tldC5hcm5Gb3JPYmplY3RzKCcqJyl9YF0sXG4gICAgICB9KSxcbiAgICApO1xuICB9XG5cbiAgaWYgKHByb3BzLmF3c1NlcnZpY2VBY2Nlc3NHZW5lcmF0b3JzKSB7XG4gICAgZm9yIChsZXQgc2VydmljZUFjY2Vzc1NwZWMgb2YgcHJvcHMuYXdzU2VydmljZUFjY2Vzc0dlbmVyYXRvcnMpIHtcbiAgICAgIGxldCBhbGxvd1N0YXRlbWVudHM6QXJyYXk8UG9saWN5U3RhdGVtZW50PiA9IHNlcnZpY2VBY2Nlc3NTcGVjLm1ha2VBbGxvd1N0YXRlbWVudHMoKTtcbiAgICAgIGs5U3RhdGVtZW50cy51bnNoaWZ0KC4uLmFsbG93U3RhdGVtZW50cyk7XG4gICAgfVxuICB9XG5cbiAgLy8gTWFrZSBEZW55IFN0YXRlbWVudFxuICBjb25zdCBkZW55RXZlcnlvbmVFbHNlVGVzdCA9IHBvbGljeUZhY3Rvcnkud2FzTGlrZVVzZWQocHJvcHMuazlEZXNpcmVkQWNjZXNzKSA/XG4gICAgJ0Fybk5vdExpa2UnIDpcbiAgICAnQXJuTm90RXF1YWxzJztcbiAgbGV0IGRlbnlFdmVyeW9uZUVsc2VTdGF0ZW1lbnQ6IFBvbGljeVN0YXRlbWVudDtcblxuICBpZiAocHJvcHMucHVibGljUmVhZEFjY2Vzcykge1xuICAgIGRlbnlFdmVyeW9uZUVsc2VTdGF0ZW1lbnQgPSBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIHNpZDogJ0RlbnlFdmVyeW9uZUVsc2UnLFxuICAgICAgZWZmZWN0OiBFZmZlY3QuREVOWSxcbiAgICAgIHByaW5jaXBhbHM6IHBvbGljeUZhY3RvcnkubWFrZURlbnlFdmVyeW9uZUVsc2VQcmluY2lwYWxzKCksXG4gICAgICBub3RBY3Rpb25zOiBbJ3MzOkdldE9iamVjdCddLFxuICAgICAgcmVzb3VyY2VzOiByZXNvdXJjZUFybnMsXG4gICAgfSk7XG4gIH0gZWxzZSB7XG4gICAgZGVueUV2ZXJ5b25lRWxzZVN0YXRlbWVudCA9IG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgc2lkOiAnRGVueUV2ZXJ5b25lRWxzZScsXG4gICAgICBlZmZlY3Q6IEVmZmVjdC5ERU5ZLFxuICAgICAgcHJpbmNpcGFsczogcG9saWN5RmFjdG9yeS5tYWtlRGVueUV2ZXJ5b25lRWxzZVByaW5jaXBhbHMoKSxcbiAgICAgIGFjdGlvbnM6IFsnczM6KiddLFxuICAgICAgcmVzb3VyY2VzOiByZXNvdXJjZUFybnMsXG4gICAgfSk7XG4gIH1cbiAgY29uc3QgYWxsQWxsb3dlZFByaW5jaXBhbEFybnMgPSBuZXcgU2V0PHN0cmluZz4ocG9saWN5RmFjdG9yeS5nZXRBbGxvd2VkUHJpbmNpcGFsQXJucyhwcm9wcy5rOURlc2lyZWRBY2Nlc3MpKTtcbiAgZm9yIChsZXQgb3JpZ0FXU1ByaW5jaXBhbCBvZiBvcmlnQWxsb3dlZEFXU1ByaW5jaXBhbHMpIHtcbiAgICBhbGxBbGxvd2VkUHJpbmNpcGFsQXJucy5hZGQob3JpZ0FXU1ByaW5jaXBhbCk7XG4gIH1cbiAgZGVueUV2ZXJ5b25lRWxzZVN0YXRlbWVudC5hZGRDb25kaXRpb24oZGVueUV2ZXJ5b25lRWxzZVRlc3QsXG4gICAgeyAnYXdzOlByaW5jaXBhbEFybic6IFsuLi5hbGxBbGxvd2VkUHJpbmNpcGFsQXJuc10gfSk7XG5cbiAgaWYgKHByb3BzLmF3c1NlcnZpY2VBY2Nlc3NHZW5lcmF0b3JzKSB7XG4gICAgZm9yIChsZXQgc2VydmljZUFjY2Vzc1NwZWMgb2YgcHJvcHMuYXdzU2VydmljZUFjY2Vzc0dlbmVyYXRvcnMpIHtcbiAgICAgIGxldCBjb25kaXRpb25zVG9FeGNlcHRGcm9tRGVueUV2ZXJ5b25lRWxzZSA9IHNlcnZpY2VBY2Nlc3NTcGVjLm1ha2VDb25kaXRpb25zVG9FeGNlcHRGcm9tRGVueUV2ZXJ5b25lRWxzZSgpO1xuICAgICAgbGV0IGNvbmRpdGlvbk9wcyA9IE9iamVjdC5rZXlzKGNvbmRpdGlvbnNUb0V4Y2VwdEZyb21EZW55RXZlcnlvbmVFbHNlKSBhcyBBcnJheTxzdHJpbmc+O1xuICAgICAgZm9yIChsZXQgY29uZGl0aW9uT3Agb2YgY29uZGl0aW9uT3BzKSB7XG4gICAgICAgIC8vIG5vdGU6IHdoZW4geW91IGNhbGwgUG9saWN5U3RhdGVtZW50I2FkZENvbmRpdGlvbiB3aXRoIHRoZSBzYW1lIGNvbmRpdGlvbk9wIChlLmcuIFN0cmluZ0VxdWFscylcbiAgICAgICAgLy8gbXVsdGlwbGUgdGltZXMsIGFkZENvbmRpdGlvbiB3aWxsIGNvbGxlY3QgdGhlIHZhbHVlcyBpbnRvIGFuIGFycmF5LlxuICAgICAgICAvLyBjLmYuIGh0dHBzOi8vZ2l0aHViLmNvbS9hd3MvYXdzLWNkay9ibG9iL21haW4vcGFja2FnZXMvYXdzLWNkay1saWIvYXdzLWlhbS9saWIvcG9saWN5LXN0YXRlbWVudC50cyNMMzY0XG4gICAgICAgIGRlbnlFdmVyeW9uZUVsc2VTdGF0ZW1lbnQuYWRkQ29uZGl0aW9uKGNvbmRpdGlvbk9wLCBjb25kaXRpb25zVG9FeGNlcHRGcm9tRGVueUV2ZXJ5b25lRWxzZVtjb25kaXRpb25PcF0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIGRlZmF1bHQgZW5jcnlwdGlvbiBtZXRob2QgdG8gU1NFLUtNUyxcbiAgLy8gYWxsb3cgb3ZlcnJpZGUgdG8gU1NFLVMzIChBRVMyNTYpXG4gIGxldCBlbmNyeXB0aW9uTWV0aG9kID0gJ2F3czprbXMnO1xuICBpZiAocHJvcHMuZW5jcnlwdGlvbikge1xuICAgIGlmIChCdWNrZXRFbmNyeXB0aW9uLlMzX01BTkFHRUQgPT0gcHJvcHMuZW5jcnlwdGlvbikge1xuICAgICAgZW5jcnlwdGlvbk1ldGhvZCA9ICdBRVMyNTYnO1xuICAgIH1cbiAgfVxuICBrOVN0YXRlbWVudHMucHVzaChcbiAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIHNpZDogJ0RlbnlJbnNlY3VyZUNvbW11bmljYXRpb25zJyxcbiAgICAgIGVmZmVjdDogRWZmZWN0LkRFTlksXG4gICAgICBwcmluY2lwYWxzOiBbbmV3IEFueVByaW5jaXBhbCgpXSxcbiAgICAgIGFjdGlvbnM6IFsnczM6KiddLFxuICAgICAgcmVzb3VyY2VzOiByZXNvdXJjZUFybnMsXG4gICAgICBjb25kaXRpb25zOiB7XG4gICAgICAgIEJvb2w6IHsgJ2F3czpTZWN1cmVUcmFuc3BvcnQnOiBmYWxzZSB9LFxuICAgICAgfSxcbiAgICB9KSxcbiAgKTtcblxuICBpZiAocHJvcHMuZW5mb3JjZUVuY3J5cHRpb25BdFJlc3QgPz8gdHJ1ZSkge1xuICAgIGs5U3RhdGVtZW50cy5wdXNoKFxuICAgICAgbmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgIHNpZDogU0lEX0RFTllfVU5FTkNSWVBURURfU1RPUkFHRSxcbiAgICAgICAgZWZmZWN0OiBFZmZlY3QuREVOWSxcbiAgICAgICAgcHJpbmNpcGFsczogW25ldyBBbnlQcmluY2lwYWwoKV0sXG4gICAgICAgIGFjdGlvbnM6IFsnczM6UHV0T2JqZWN0JywgJ3MzOlJlcGxpY2F0ZU9iamVjdCddLFxuICAgICAgICByZXNvdXJjZXM6IHJlc291cmNlQXJucyxcbiAgICAgICAgY29uZGl0aW9uczoge1xuICAgICAgICAgIE51bGw6IHsgJ3MzOngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24nOiB0cnVlIH0sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAgKSxcbiAgICAgIG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBzaWQ6IFNJRF9ERU5ZX1VORVhQRUNURURfRU5DUllQVElPTl9NRVRIT0QsXG4gICAgICAgIGVmZmVjdDogRWZmZWN0LkRFTlksXG4gICAgICAgIHByaW5jaXBhbHM6IFtuZXcgQW55UHJpbmNpcGFsKCldLFxuICAgICAgICBhY3Rpb25zOiBbJ3MzOlB1dE9iamVjdCcsICdzMzpSZXBsaWNhdGVPYmplY3QnXSxcbiAgICAgICAgcmVzb3VyY2VzOiByZXNvdXJjZUFybnMsXG4gICAgICAgIGNvbmRpdGlvbnM6IHtcbiAgICAgICAgICBTdHJpbmdOb3RFcXVhbHM6IHsgJ3MzOngtYW16LXNlcnZlci1zaWRlLWVuY3J5cHRpb24nOiBlbmNyeXB0aW9uTWV0aG9kIH0sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAgKSk7XG4gIH1cblxuICBrOVN0YXRlbWVudHMucHVzaChkZW55RXZlcnlvbmVFbHNlU3RhdGVtZW50KTtcblxuICBmb3IgKGxldCBzdGF0ZW1lbnQgb2YgazlTdGF0ZW1lbnRzKSB7XG4gICAgbGV0IGFkZFRvUmVzb3VyY2VQb2xpY3lSZXN1bHQgPSBwcm9wcy5idWNrZXQuYWRkVG9SZXNvdXJjZVBvbGljeShzdGF0ZW1lbnQpO1xuICAgIGFkZFRvUmVzb3VyY2VQb2xpY3lSZXN1bHRzLnB1c2goYWRkVG9SZXNvdXJjZVBvbGljeVJlc3VsdCk7XG4gIH1cblxuICBwb2xpY3kuZG9jdW1lbnQudmFsaWRhdGVGb3JSZXNvdXJjZVBvbGljeSgpO1xuXG4gIHJldHVybiBhZGRUb1Jlc291cmNlUG9saWN5UmVzdWx0cztcbn1cbiJdfQ==