cdk-nag
Version:
Check CDK v2 applications for best practices using a combination on available rule packs.
108 lines • 14 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
const path_1 = require("path");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const aws_s3_1 = require("aws-cdk-lib/aws-s3");
const nag_rules_1 = require("../../nag-rules");
/**
* S3 static website buckets do not have an open world bucket policy and use CloudFront Origin Access Identities in the bucket policy for limited getObject and/or putObject permissions
* @param node the CfnResource to check
*/
exports.default = Object.defineProperty((node) => {
if (node instanceof aws_s3_1.CfnBucket) {
if (node.websiteConfiguration !== undefined) {
const bucketLogicalId = nag_rules_1.NagRules.resolveResourceFromInstrinsic(node, node.ref);
const bucketName = aws_cdk_lib_1.Stack.of(node).resolve(node.bucketName);
let found = false;
for (const child of aws_cdk_lib_1.Stack.of(node).node.findAll()) {
if (child instanceof aws_s3_1.CfnBucketPolicy) {
if (isMatchingCompliantPolicy(child, bucketLogicalId, bucketName)) {
found = true;
}
}
}
if (!found) {
return nag_rules_1.NagRuleCompliance.NON_COMPLIANT;
}
}
return nag_rules_1.NagRuleCompliance.COMPLIANT;
}
else {
return nag_rules_1.NagRuleCompliance.NOT_APPLICABLE;
}
}, 'name', { value: path_1.parse(__filename).name });
/**
* Helper function to check whether the Bucket Policy does not allow for open access and uses a restricted OAI Policy for access on the given bucket.
* @param node The CfnBucketPolicy to check.
* @param bucketLogicalId The Cfn Logical ID of the bucket.
* @param bucketName The name of the bucket.
* @returns Whether the CfnBucketPolicy allows for open access on the given bucket.
*/
function isMatchingCompliantPolicy(node, bucketLogicalId, bucketName) {
const bucket = nag_rules_1.NagRules.resolveResourceFromInstrinsic(node, node.bucket);
if (bucket !== bucketLogicalId && bucket !== bucketName) {
return false;
}
const resolvedPolicyDocument = aws_cdk_lib_1.Stack.of(node).resolve(node.policyDocument);
let found = false;
for (const statement of resolvedPolicyDocument.Statement) {
const resolvedStatement = aws_cdk_lib_1.Stack.of(node).resolve(statement);
if (resolvedStatement.Effect === 'Allow') {
if (checkStarPrincipals(resolvedStatement.Principal)) {
return false;
}
if (checkOAIPrincipal(resolvedStatement.Principal)) {
if (checkMatchingActions(normalizeArray(resolvedStatement.Action))) {
found = true;
}
else {
return false;
}
}
}
}
return found;
}
/**
* Helper function to check whether the Bucket Policy applies to all principals
* @param node The CfnBucketPolicy to check
* @param principal The principals in the bucket policy
* @returns Whether the CfnBucketPolicy applies to all principals
*/
function checkStarPrincipals(principals) {
return JSON.stringify(principals ?? '').includes('*');
}
/**
* Helper function to check whether the Bucket Policy applies to a CloudFront OAI
* @param node The CfnBucketPolicy to check
* @param principal The principals in the bucket policy
* @returns Whether the CfnBucketPolicy applies to a CloudFront OAI
*/
function checkOAIPrincipal(principals) {
const usesAWSPrincipalOAI = JSON.stringify(principals.AWS ?? '').includes('CloudFront Origin Access Identity');
const usesCanonicalUserPrincipal = !!principals?.CanonicalUser;
return usesAWSPrincipalOAI || usesCanonicalUserPrincipal;
}
/**
* Helper function to check whether the statement applies to only GetObject and/or PutObject actions
* @param node The statement to check
* @param actions The action in the bucket statement
* @returns Whether the statement applies to only GetObject and/or PutObject actions
*/
function checkMatchingActions(actions) {
for (const action of actions) {
if (action.toLowerCase() !== 's3:getobject' &&
action.toLowerCase() !== 's3:putobject') {
return false;
}
}
return true;
}
function normalizeArray(arrOrStr) {
return Array.isArray(arrOrStr) ? arrOrStr : [arrOrStr];
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUzNXZWJCdWNrZXRPQUlBY2Nlc3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcnVsZXMvczMvUzNXZWJCdWNrZXRPQUlBY2Nlc3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQTs7O0VBR0U7QUFDRiwrQkFBNkI7QUFDN0IsNkNBQWlEO0FBQ2pELCtDQUFnRTtBQUNoRSwrQ0FBOEQ7QUFFOUQ7OztHQUdHO0FBQ0gsa0JBQWUsTUFBTSxDQUFDLGNBQWMsQ0FDbEMsQ0FBQyxJQUFpQixFQUFxQixFQUFFO0lBQ3ZDLElBQUksSUFBSSxZQUFZLGtCQUFTLEVBQUU7UUFDN0IsSUFBSSxJQUFJLENBQUMsb0JBQW9CLEtBQUssU0FBUyxFQUFFO1lBQzNDLE1BQU0sZUFBZSxHQUFHLG9CQUFRLENBQUMsNkJBQTZCLENBQzVELElBQUksRUFDSixJQUFJLENBQUMsR0FBRyxDQUNULENBQUM7WUFDRixNQUFNLFVBQVUsR0FBRyxtQkFBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQzNELElBQUksS0FBSyxHQUFHLEtBQUssQ0FBQztZQUNsQixLQUFLLE1BQU0sS0FBSyxJQUFJLG1CQUFLLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRTtnQkFDakQsSUFBSSxLQUFLLFlBQVksd0JBQWUsRUFBRTtvQkFDcEMsSUFBSSx5QkFBeUIsQ0FBQyxLQUFLLEVBQUUsZUFBZSxFQUFFLFVBQVUsQ0FBQyxFQUFFO3dCQUNqRSxLQUFLLEdBQUcsSUFBSSxDQUFDO3FCQUNkO2lCQUNGO2FBQ0Y7WUFDRCxJQUFJLENBQUMsS0FBSyxFQUFFO2dCQUNWLE9BQU8sNkJBQWlCLENBQUMsYUFBYSxDQUFDO2FBQ3hDO1NBQ0Y7UUFDRCxPQUFPLDZCQUFpQixDQUFDLFNBQVMsQ0FBQztLQUNwQztTQUFNO1FBQ0wsT0FBTyw2QkFBaUIsQ0FBQyxjQUFjLENBQUM7S0FDekM7QUFDSCxDQUFDLEVBQ0QsTUFBTSxFQUNOLEVBQUUsS0FBSyxFQUFFLFlBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FDbEMsQ0FBQztBQUVGOzs7Ozs7R0FNRztBQUNILFNBQVMseUJBQXlCLENBQ2hDLElBQXFCLEVBQ3JCLGVBQXVCLEVBQ3ZCLFVBQThCO0lBRTlCLE1BQU0sTUFBTSxHQUFHLG9CQUFRLENBQUMsNkJBQTZCLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUN6RSxJQUFJLE1BQU0sS0FBSyxlQUFlLElBQUksTUFBTSxLQUFLLFVBQVUsRUFBRTtRQUN2RCxPQUFPLEtBQUssQ0FBQztLQUNkO0lBQ0QsTUFBTSxzQkFBc0IsR0FBRyxtQkFBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBQzNFLElBQUksS0FBSyxHQUFHLEtBQUssQ0FBQztJQUNsQixLQUFLLE1BQU0sU0FBUyxJQUFJLHNCQUFzQixDQUFDLFNBQVMsRUFBRTtRQUN4RCxNQUFNLGlCQUFpQixHQUFHLG1CQUFLLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM1RCxJQUFJLGlCQUFpQixDQUFDLE1BQU0sS0FBSyxPQUFPLEVBQUU7WUFDeEMsSUFBSSxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsRUFBRTtnQkFDcEQsT0FBTyxLQUFLLENBQUM7YUFDZDtZQUNELElBQUksaUJBQWlCLENBQUMsaUJBQWlCLENBQUMsU0FBUyxDQUFDLEVBQUU7Z0JBQ2xELElBQUksb0JBQW9CLENBQUMsY0FBYyxDQUFDLGlCQUFpQixDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUU7b0JBQ2xFLEtBQUssR0FBRyxJQUFJLENBQUM7aUJBQ2Q7cUJBQU07b0JBQ0wsT0FBTyxLQUFLLENBQUM7aUJBQ2Q7YUFDRjtTQUNGO0tBQ0Y7SUFDRCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQVMsbUJBQW1CLENBQUMsVUFBZTtJQUMxQyxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN4RCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLGlCQUFpQixDQUFDLFVBQWU7SUFDeEMsTUFBTSxtQkFBbUIsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxHQUFHLElBQUksRUFBRSxDQUFDLENBQUMsUUFBUSxDQUN2RSxtQ0FBbUMsQ0FDcEMsQ0FBQztJQUNGLE1BQU0sMEJBQTBCLEdBQUcsQ0FBQyxDQUFDLFVBQVUsRUFBRSxhQUFhLENBQUM7SUFDL0QsT0FBTyxtQkFBbUIsSUFBSSwwQkFBMEIsQ0FBQztBQUMzRCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLG9CQUFvQixDQUFDLE9BQWlCO0lBQzdDLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxFQUFFO1FBQzVCLElBQ0UsTUFBTSxDQUFDLFdBQVcsRUFBRSxLQUFLLGNBQWM7WUFDdkMsTUFBTSxDQUFDLFdBQVcsRUFBRSxLQUFLLGNBQWMsRUFDdkM7WUFDQSxPQUFPLEtBQUssQ0FBQztTQUNkO0tBQ0Y7SUFDRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRCxTQUFTLGNBQWMsQ0FBSSxRQUFpQjtJQUMxQyxPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUN6RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCBBbWF6b24uY29tLCBJbmMuIG9yIGl0cyBhZmZpbGlhdGVzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcbiovXG5pbXBvcnQgeyBwYXJzZSB9IGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgQ2ZuUmVzb3VyY2UsIFN0YWNrIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgQ2ZuQnVja2V0LCBDZm5CdWNrZXRQb2xpY3kgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtczMnO1xuaW1wb3J0IHsgTmFnUnVsZUNvbXBsaWFuY2UsIE5hZ1J1bGVzIH0gZnJvbSAnLi4vLi4vbmFnLXJ1bGVzJztcblxuLyoqXG4gKiBTMyBzdGF0aWMgd2Vic2l0ZSBidWNrZXRzIGRvIG5vdCBoYXZlIGFuIG9wZW4gd29ybGQgYnVja2V0IHBvbGljeSBhbmQgdXNlIENsb3VkRnJvbnQgT3JpZ2luIEFjY2VzcyBJZGVudGl0aWVzIGluIHRoZSBidWNrZXQgcG9saWN5IGZvciBsaW1pdGVkIGdldE9iamVjdCBhbmQvb3IgcHV0T2JqZWN0IHBlcm1pc3Npb25zXG4gKiBAcGFyYW0gbm9kZSB0aGUgQ2ZuUmVzb3VyY2UgdG8gY2hlY2tcbiAqL1xuZXhwb3J0IGRlZmF1bHQgT2JqZWN0LmRlZmluZVByb3BlcnR5KFxuICAobm9kZTogQ2ZuUmVzb3VyY2UpOiBOYWdSdWxlQ29tcGxpYW5jZSA9PiB7XG4gICAgaWYgKG5vZGUgaW5zdGFuY2VvZiBDZm5CdWNrZXQpIHtcbiAgICAgIGlmIChub2RlLndlYnNpdGVDb25maWd1cmF0aW9uICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29uc3QgYnVja2V0TG9naWNhbElkID0gTmFnUnVsZXMucmVzb2x2ZVJlc291cmNlRnJvbUluc3RyaW5zaWMoXG4gICAgICAgICAgbm9kZSxcbiAgICAgICAgICBub2RlLnJlZlxuICAgICAgICApO1xuICAgICAgICBjb25zdCBidWNrZXROYW1lID0gU3RhY2sub2Yobm9kZSkucmVzb2x2ZShub2RlLmJ1Y2tldE5hbWUpO1xuICAgICAgICBsZXQgZm91bmQgPSBmYWxzZTtcbiAgICAgICAgZm9yIChjb25zdCBjaGlsZCBvZiBTdGFjay5vZihub2RlKS5ub2RlLmZpbmRBbGwoKSkge1xuICAgICAgICAgIGlmIChjaGlsZCBpbnN0YW5jZW9mIENmbkJ1Y2tldFBvbGljeSkge1xuICAgICAgICAgICAgaWYgKGlzTWF0Y2hpbmdDb21wbGlhbnRQb2xpY3koY2hpbGQsIGJ1Y2tldExvZ2ljYWxJZCwgYnVja2V0TmFtZSkpIHtcbiAgICAgICAgICAgICAgZm91bmQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoIWZvdW5kKSB7XG4gICAgICAgICAgcmV0dXJuIE5hZ1J1bGVDb21wbGlhbmNlLk5PTl9DT01QTElBTlQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBOYWdSdWxlQ29tcGxpYW5jZS5DT01QTElBTlQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBOYWdSdWxlQ29tcGxpYW5jZS5OT1RfQVBQTElDQUJMRTtcbiAgICB9XG4gIH0sXG4gICduYW1lJyxcbiAgeyB2YWx1ZTogcGFyc2UoX19maWxlbmFtZSkubmFtZSB9XG4pO1xuXG4vKipcbiAqIEhlbHBlciBmdW5jdGlvbiB0byBjaGVjayB3aGV0aGVyIHRoZSBCdWNrZXQgUG9saWN5IGRvZXMgbm90IGFsbG93IGZvciBvcGVuIGFjY2VzcyAgYW5kIHVzZXMgYSByZXN0cmljdGVkIE9BSSBQb2xpY3kgZm9yIGFjY2VzcyBvbiB0aGUgZ2l2ZW4gYnVja2V0LlxuICogQHBhcmFtIG5vZGUgVGhlIENmbkJ1Y2tldFBvbGljeSB0byBjaGVjay5cbiAqIEBwYXJhbSBidWNrZXRMb2dpY2FsSWQgVGhlIENmbiBMb2dpY2FsIElEIG9mIHRoZSBidWNrZXQuXG4gKiBAcGFyYW0gYnVja2V0TmFtZSBUaGUgbmFtZSBvZiB0aGUgYnVja2V0LlxuICogQHJldHVybnMgV2hldGhlciB0aGUgQ2ZuQnVja2V0UG9saWN5IGFsbG93cyBmb3Igb3BlbiBhY2Nlc3Mgb24gdGhlIGdpdmVuIGJ1Y2tldC5cbiAqL1xuZnVuY3Rpb24gaXNNYXRjaGluZ0NvbXBsaWFudFBvbGljeShcbiAgbm9kZTogQ2ZuQnVja2V0UG9saWN5LFxuICBidWNrZXRMb2dpY2FsSWQ6IHN0cmluZyxcbiAgYnVja2V0TmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkXG4pOiBib29sZWFuIHtcbiAgY29uc3QgYnVja2V0ID0gTmFnUnVsZXMucmVzb2x2ZVJlc291cmNlRnJvbUluc3RyaW5zaWMobm9kZSwgbm9kZS5idWNrZXQpO1xuICBpZiAoYnVja2V0ICE9PSBidWNrZXRMb2dpY2FsSWQgJiYgYnVja2V0ICE9PSBidWNrZXROYW1lKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGNvbnN0IHJlc29sdmVkUG9saWN5RG9jdW1lbnQgPSBTdGFjay5vZihub2RlKS5yZXNvbHZlKG5vZGUucG9saWN5RG9jdW1lbnQpO1xuICBsZXQgZm91bmQgPSBmYWxzZTtcbiAgZm9yIChjb25zdCBzdGF0ZW1lbnQgb2YgcmVzb2x2ZWRQb2xpY3lEb2N1bWVudC5TdGF0ZW1lbnQpIHtcbiAgICBjb25zdCByZXNvbHZlZFN0YXRlbWVudCA9IFN0YWNrLm9mKG5vZGUpLnJlc29sdmUoc3RhdGVtZW50KTtcbiAgICBpZiAocmVzb2x2ZWRTdGF0ZW1lbnQuRWZmZWN0ID09PSAnQWxsb3cnKSB7XG4gICAgICBpZiAoY2hlY2tTdGFyUHJpbmNpcGFscyhyZXNvbHZlZFN0YXRlbWVudC5QcmluY2lwYWwpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGlmIChjaGVja09BSVByaW5jaXBhbChyZXNvbHZlZFN0YXRlbWVudC5QcmluY2lwYWwpKSB7XG4gICAgICAgIGlmIChjaGVja01hdGNoaW5nQWN0aW9ucyhub3JtYWxpemVBcnJheShyZXNvbHZlZFN0YXRlbWVudC5BY3Rpb24pKSkge1xuICAgICAgICAgIGZvdW5kID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIGZvdW5kO1xufVxuXG4vKipcbiAqIEhlbHBlciBmdW5jdGlvbiB0byBjaGVjayB3aGV0aGVyIHRoZSBCdWNrZXQgUG9saWN5IGFwcGxpZXMgdG8gYWxsIHByaW5jaXBhbHNcbiAqIEBwYXJhbSBub2RlIFRoZSBDZm5CdWNrZXRQb2xpY3kgdG8gY2hlY2tcbiAqIEBwYXJhbSBwcmluY2lwYWwgVGhlIHByaW5jaXBhbHMgaW4gdGhlIGJ1Y2tldCBwb2xpY3lcbiAqIEByZXR1cm5zIFdoZXRoZXIgdGhlIENmbkJ1Y2tldFBvbGljeSBhcHBsaWVzIHRvIGFsbCBwcmluY2lwYWxzXG4gKi9cbmZ1bmN0aW9uIGNoZWNrU3RhclByaW5jaXBhbHMocHJpbmNpcGFsczogYW55KTogYm9vbGVhbiB7XG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShwcmluY2lwYWxzID8/ICcnKS5pbmNsdWRlcygnKicpO1xufVxuXG4vKipcbiAqIEhlbHBlciBmdW5jdGlvbiB0byBjaGVjayB3aGV0aGVyIHRoZSBCdWNrZXQgUG9saWN5IGFwcGxpZXMgdG8gYSBDbG91ZEZyb250IE9BSVxuICogQHBhcmFtIG5vZGUgVGhlIENmbkJ1Y2tldFBvbGljeSB0byBjaGVja1xuICogQHBhcmFtIHByaW5jaXBhbCBUaGUgcHJpbmNpcGFscyBpbiB0aGUgYnVja2V0IHBvbGljeVxuICogQHJldHVybnMgV2hldGhlciB0aGUgQ2ZuQnVja2V0UG9saWN5IGFwcGxpZXMgdG8gYSBDbG91ZEZyb250IE9BSVxuICovXG5mdW5jdGlvbiBjaGVja09BSVByaW5jaXBhbChwcmluY2lwYWxzOiBhbnkpOiBib29sZWFuIHtcbiAgY29uc3QgdXNlc0FXU1ByaW5jaXBhbE9BSSA9IEpTT04uc3RyaW5naWZ5KHByaW5jaXBhbHMuQVdTID8/ICcnKS5pbmNsdWRlcyhcbiAgICAnQ2xvdWRGcm9udCBPcmlnaW4gQWNjZXNzIElkZW50aXR5J1xuICApO1xuICBjb25zdCB1c2VzQ2Fub25pY2FsVXNlclByaW5jaXBhbCA9ICEhcHJpbmNpcGFscz8uQ2Fub25pY2FsVXNlcjtcbiAgcmV0dXJuIHVzZXNBV1NQcmluY2lwYWxPQUkgfHwgdXNlc0Nhbm9uaWNhbFVzZXJQcmluY2lwYWw7XG59XG5cbi8qKlxuICogSGVscGVyIGZ1bmN0aW9uIHRvIGNoZWNrIHdoZXRoZXIgdGhlIHN0YXRlbWVudCBhcHBsaWVzIHRvIG9ubHkgR2V0T2JqZWN0IGFuZC9vciBQdXRPYmplY3QgYWN0aW9uc1xuICogQHBhcmFtIG5vZGUgVGhlIHN0YXRlbWVudCB0byBjaGVja1xuICogQHBhcmFtIGFjdGlvbnMgVGhlIGFjdGlvbiBpbiB0aGUgYnVja2V0IHN0YXRlbWVudFxuICogQHJldHVybnMgV2hldGhlciB0aGUgc3RhdGVtZW50IGFwcGxpZXMgdG8gb25seSBHZXRPYmplY3QgYW5kL29yIFB1dE9iamVjdCBhY3Rpb25zXG4gKi9cbmZ1bmN0aW9uIGNoZWNrTWF0Y2hpbmdBY3Rpb25zKGFjdGlvbnM6IHN0cmluZ1tdKTogYm9vbGVhbiB7XG4gIGZvciAoY29uc3QgYWN0aW9uIG9mIGFjdGlvbnMpIHtcbiAgICBpZiAoXG4gICAgICBhY3Rpb24udG9Mb3dlckNhc2UoKSAhPT0gJ3MzOmdldG9iamVjdCcgJiZcbiAgICAgIGFjdGlvbi50b0xvd2VyQ2FzZSgpICE9PSAnczM6cHV0b2JqZWN0J1xuICAgICkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdHJ1ZTtcbn1cblxuZnVuY3Rpb24gbm9ybWFsaXplQXJyYXk8VD4oYXJyT3JTdHI6IFRbXSB8IFQpOiBUW10ge1xuICByZXR1cm4gQXJyYXkuaXNBcnJheShhcnJPclN0cikgPyBhcnJPclN0ciA6IFthcnJPclN0cl07XG59XG4iXX0=
;