UNPKG

waf-http-api

Version:

A CDK construct that fronts an HTTP API with a CloudFront distribution and protects it with AWS WAF.

213 lines 30.9 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.WafHttpApi = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const crypto = require("crypto"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const cloudfront = require("aws-cdk-lib/aws-cloudfront"); const origins = require("aws-cdk-lib/aws-cloudfront-origins"); const wafv2 = require("aws-cdk-lib/aws-wafv2"); const constructs_1 = require("constructs"); /** * @class WafHttpApi * @extends Construct * @description A CDK construct that fronts an AWS HTTP API with a CloudFront distribution * and protects it with AWS WAF. This enhances security and performance by * adding a global CDN layer and web application firewall capabilities. * It also injects a secret header from CloudFront to the origin to allow * for origin verification by a Lambda Authorizer or similar mechanism. */ class WafHttpApi extends constructs_1.Construct { /** * @constructor * @param {Construct} scope The scope in which to define this construct (e.g., a CDK Stack). * @param {string} id The unique identifier for this construct within its scope. * @param {WafHttpApiProps} props The properties required to configure this construct, * including the target HTTP API and optional WAF rules. */ constructor(scope, id, props) { super(scope, id); /** * @example * // Example usage within a CDK Stack: * import { Stack, StackProps } from 'aws-cdk-lib'; * import { HttpApi, HttpMethod, HttpIntegration } from 'aws-cdk-lib/aws-apigatewayv2'; * import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations'; * import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; * import { Runtime } from 'aws-cdk-lib/aws-lambda'; * import { WafHttpApi } from './waf-http-api'; // Assuming your construct is in this path * * // Define a simple Lambda function to integrate with the API Gateway * const myLambda = new NodejsFunction(this, 'MyApiHandler', { * runtime: Runtime.NODEJS_18_X, * handler: 'handler', * entry: 'path/to/your/lambda/code.ts', // Specify the path to your Lambda function code * }); * * // Create an HTTP API Gateway * const httpApi = new HttpApi(this, 'MyHttpApi', { * description: 'My example HTTP API', * }); * * // Add a route to the HTTP API that integrates with the Lambda function * httpApi.addRoutes({ * path: '/hello', * methods: [HttpMethod.GET], * integration: new HttpLambdaIntegration('MyLambdaIntegration', myLambda), * }); * * // Instantiate the WafHttpApi construct to protect the HTTP API * const protectedApi = new WafHttpApi(this, 'ProtectedMyApi', { * httpApi: httpApi, * // Optionally, provide custom WAF rules: * // wafRules: [ * // { * // name: 'MyCustomIpBlockRule', * // priority: 10, * // statement: { * // ipSetReferenceStatement: { arn: 'arn:aws:wafv2:...' } * // }, * // action: { block: {} }, * // visibilityConfig: { * // cloudWatchMetricsEnabled: true, * // metricName: 'MyCustomIpBlock', * // sampledRequestsEnabled: true, * // }, * // }, * // ], * }); * * // Output the CloudFront URL of the protected API * new cdk.CfnOutput(this, 'ProtectedApiEndpoint', { * value: protectedApi.distribution.distributionDomainName, * description: 'The CloudFront URL for the protected API endpoint', * }); * * // Output the secret header value for use in a Lambda authorizer if needed * new cdk.CfnOutput(this, 'OriginVerificationSecret', { * value: protectedApi.secretHeaderValue, * description: 'Secret value to verify CloudFront origin requests', * }); */ // Generate a cryptographically strong random hex string for the secret header value. // This ensures that the secret is unique and difficult to guess, enhancing security // when used for origin verification. this.secretHeaderValue = crypto.randomBytes(16).toString("hex"); // Determine which WAF rules to apply. If custom rules are provided via props, use them. // Otherwise, fall back to the default managed rules defined in `createDefaultRules()`. const rules = props.wafRules ?? this.createDefaultRules(); // 1. Create the AWS WAF WebACL (Web Access Control List) // This WebACL will be associated with the CloudFront distribution to filter web traffic. const webAcl = new wafv2.CfnWebACL(this, id + "WebAcl", { // Default action for requests that don't match any rules. 'allow' means they pass through. defaultAction: { allow: {} }, // The scope MUST be 'CLOUDFRONT' for a WebACL to be associated with a CloudFront distribution. scope: "CLOUDFRONT", // Configuration for CloudWatch metrics and sampled requests, useful for monitoring WAF activity. visibilityConfig: { cloudWatchMetricsEnabled: true, // Enable metrics to view WAF performance in CloudWatch. metricName: `${id}-Waf`, // Unique metric name for this WebACL. sampledRequestsEnabled: true, // Enable sampling of requests for debugging and analysis. }, // The array of rules that define how to inspect and handle web requests. rules: rules, }); // 2. Create the CloudFront distribution // This distribution acts as the public-facing endpoint for the HTTP API, // providing CDN benefits like caching and reduced latency, and integrating with WAF. this.distribution = new cloudfront.Distribution(this, id + "ApiDistribution", { comment: `CloudFront distribution for HTTP API: ${props.httpApi.httpApiId}`, // Descriptive comment for the distribution. // Associate the created WAF WebACL with this CloudFront distribution. webAclId: webAcl.attrArn, // Enforce a minimum TLS security policy for connections between viewers and CloudFront. minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021, // Define the default behavior for requests hitting this distribution. defaultBehavior: { // Configure the origin (backend server) for the distribution. // `Fn.select(2, Fn.split("/", props.httpApi.url!))` extracts the domain name // from the HTTP API's URL (e.g., "abcdef.execute-api.us-east-1.amazonaws.com"). origin: new origins.HttpOrigin(aws_cdk_lib_1.Fn.select(2, aws_cdk_lib_1.Fn.split("/", props.httpApi.url)), { // Enforce HTTPS-only communication between CloudFront and the origin. protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY, // Add the custom secret header to every request CloudFront forwards to the origin. // This is crucial for origin verification. customHeaders: { [WafHttpApi.SECRET_HEADER_NAME]: this.secretHeaderValue, }, }), // Redirect all HTTP viewer requests to HTTPS to ensure secure communication. viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, // Allow all HTTP methods (GET, POST, PUT, DELETE, etc.) to be forwarded to the origin. allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL, // Disable caching for API requests by default, as API responses are typically dynamic // and should not be cached by the CDN. cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED, // Forward all viewer headers, query strings, and cookies to the origin, // EXCEPT for the 'Host' header. The 'Host' header needs to be the API Gateway's // host to ensure correct routing within API Gateway. originRequestPolicy: cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER, }, }); } /** * @private * @method createDefaultRules * @description Creates a default set of AWS Managed Rules for the WAF WebACL. * These rules provide a good baseline of protection against common threats. * @returns {wafv2.CfnWebACL.RuleProperty[]} An array of WAF rule properties. */ createDefaultRules() { return [ { name: "AWS-AWSManagedRulesAmazonIpReputationList", priority: 1, // Rules are evaluated in order of priority (lower number first). statement: { // Specifies a managed rule group provided by AWS. managedRuleGroupStatement: { vendorName: "AWS", name: "AWSManagedRulesAmazonIpReputationList", // This rule group blocks known malicious IP addresses. }, }, // The `none` action means the rule group's default action (usually 'block') will apply. overrideAction: { none: {} }, // Configuration for CloudWatch metrics and sampled requests for this specific rule. visibilityConfig: { cloudWatchMetricsEnabled: true, metricName: "awsManagedRulesAmazonIpReputationList", sampledRequestsEnabled: true, }, }, { name: "AWS-AWSManagedRulesCommonRuleSet", priority: 2, statement: { managedRuleGroupStatement: { vendorName: "AWS", name: "AWSManagedRulesCommonRuleSet", // This rule group protects against a broad range of common web exploits. }, }, overrideAction: { none: {} }, visibilityConfig: { cloudWatchMetricsEnabled: true, metricName: "awsManagedRulesCommonRuleSet", sampledRequestsEnabled: true, }, }, ]; } } exports.WafHttpApi = WafHttpApi; _a = JSII_RTTI_SYMBOL_1; WafHttpApi[_a] = { fqn: "waf-http-api.WafHttpApi", version: "1.0.0" }; /** * @static * @readonly * @property {string} SECRET_HEADER_NAME * @description The name of the custom header CloudFront will add to requests * forwarded to the origin. This header can be used by your backend (e.g., * a Lambda Authorizer for API Gateway) to verify that the request originated * from CloudFront and not directly from the internet. */ WafHttpApi.SECRET_HEADER_NAME = "X-Origin-Verify"; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxpQ0FBaUM7QUFDakMsNkNBQWlDO0FBRWpDLHlEQUF5RDtBQUN6RCw4REFBOEQ7QUFDOUQsK0NBQStDO0FBQy9DLDJDQUF1QztBQXdCdkM7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFhLFVBQVcsU0FBUSxzQkFBUztJQThCdkM7Ozs7OztPQU1HO0lBQ0gsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUFzQjtRQUM5RCxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRWpCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBNkRHO1FBRUgscUZBQXFGO1FBQ3JGLG9GQUFvRjtRQUNwRixxQ0FBcUM7UUFDckMsSUFBSSxDQUFDLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRWhFLHdGQUF3RjtRQUN4Rix1RkFBdUY7UUFDdkYsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUUxRCx5REFBeUQ7UUFDekQseUZBQXlGO1FBQ3pGLE1BQU0sTUFBTSxHQUFHLElBQUksS0FBSyxDQUFDLFNBQVMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxHQUFHLFFBQVEsRUFBRTtZQUN0RCwyRkFBMkY7WUFDM0YsYUFBYSxFQUFFLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRTtZQUM1QiwrRkFBK0Y7WUFDL0YsS0FBSyxFQUFFLFlBQVk7WUFDbkIsaUdBQWlHO1lBQ2pHLGdCQUFnQixFQUFFO2dCQUNoQix3QkFBd0IsRUFBRSxJQUFJLEVBQUUsd0RBQXdEO2dCQUN4RixVQUFVLEVBQUUsR0FBRyxFQUFFLE1B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Q0osQ0FBQzs7QUE3TkgsZ0NBOE5DOzs7QUE3TkM7Ozs7Ozs7O0dBUUc7QUFDb0IsNkJBQWtCLEdBQUcsaUJBQWlCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSBcImNyeXB0b1wiO1xuaW1wb3J0IHsgRm4gfSBmcm9tIFwiYXdzLWNkay1saWJcIjtcbmltcG9ydCB7IEh0dHBBcGkgfSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXl2MlwiO1xuaW1wb3J0ICogYXMgY2xvdWRmcm9udCBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWNsb3VkZnJvbnRcIjtcbmltcG9ydCAqIGFzIG9yaWdpbnMgZnJvbSBcImF3cy1jZGstbGliL2F3cy1jbG91ZGZyb250LW9yaWdpbnNcIjtcbmltcG9ydCAqIGFzIHdhZnYyIGZyb20gXCJhd3MtY2RrLWxpYi9hd3Mtd2FmdjJcIjtcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gXCJjb25zdHJ1Y3RzXCI7XG5cbi8qKlxuICogQGludGVyZmFjZSBXYWZIdHRwQXBpUHJvcHNcbiAqIEBkZXNjcmlwdGlvbiBQcm9wZXJ0aWVzIGZvciB0aGUgYFdhZkZvckh0dHBBcGlgIGNvbnN0cnVjdC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBXYWZIdHRwQXBpUHJvcHMge1xuICAvKipcbiAgICogVGhlIEhUVFAgQVBJIHRvIGJlIHByb3RlY3RlZCBieSB0aGUgV0FGIGFuZCBDbG91ZEZyb250LlxuICAgKiBUaGlzIHNob3VsZCBiZSBhbiBpbnN0YW5jZSBvZiBgYXdzLWNkay1saWIvYXdzLWFwaWdhdGV3YXl2Mi5IdHRwQXBpYC5cbiAgICogQHR5cGUge0h0dHBBcGl9XG4gICAqL1xuICByZWFkb25seSBodHRwQXBpOiBIdHRwQXBpO1xuICAvKipcbiAgICogT3B0aW9uYWw6IEN1c3RvbSBXQUYgcnVsZXMgdG8gYXBwbHkgdG8gdGhlIFdlYkFDTC5cbiAgICogSWYgbm90IHByb3ZpZGVkLCBhIGRlZmF1bHQgc2V0IG9mIEFXUyBNYW5hZ2VkIFJ1bGVzIHdpbGwgYmUgdXNlZCxcbiAgICogc3BlY2lmaWNhbGx5IFwiQVdTTWFuYWdlZFJ1bGVzQW1hem9uSXBSZXB1dGF0aW9uTGlzdFwiIGFuZCBcIkFXU01hbmFnZWRSdWxlc0NvbW1vblJ1bGVTZXRcIi5cbiAgICogVGhlc2UgcnVsZXMgaGVscCBwcm90ZWN0IGFnYWluc3QgY29tbW9uIHdlYiBleHBsb2l0cyBhbmQgdW53YW50ZWQgdHJhZmZpYy5cbiAgICogQHR5cGUge3dhZnYyLkNmbldlYkFDTC5SdWxlUHJvcGVydHlbXX1cbiAgICogQGRlZmF1bHQgQVdTIE1hbmFnZWQgUnVsZXMgKEFtYXpvbklwUmVwdXRhdGlvbkxpc3QsIENvbW1vblJ1bGVTZXQpXG4gICAqL1xuICByZWFkb25seSB3YWZSdWxlcz86IHdhZnYyLkNmbldlYkFDTC5SdWxlUHJvcGVydHlbXTtcbn1cblxuLyoqXG4gKiBAY2xhc3MgV2FmSHR0cEFwaVxuICogQGV4dGVuZHMgQ29uc3RydWN0XG4gKiBAZGVzY3JpcHRpb24gQSBDREsgY29uc3RydWN0IHRoYXQgZnJvbnRzIGFuIEFXUyBIVFRQIEFQSSB3aXRoIGEgQ2xvdWRGcm9udCBkaXN0cmlidXRpb25cbiAqIGFuZCBwcm90ZWN0cyBpdCB3aXRoIEFXUyBXQUYuIFRoaXMgZW5oYW5jZXMgc2VjdXJpdHkgYW5kIHBlcmZvcm1hbmNlIGJ5XG4gKiBhZGRpbmcgYSBnbG9iYWwgQ0ROIGxheWVyIGFuZCB3ZWIgYXBwbGljYXRpb24gZmlyZXdhbGwgY2FwYWJpbGl0aWVzLlxuICogSXQgYWxzbyBpbmplY3RzIGEgc2VjcmV0IGhlYWRlciBmcm9tIENsb3VkRnJvbnQgdG8gdGhlIG9yaWdpbiB0byBhbGxvd1xuICogZm9yIG9yaWdpbiB2ZXJpZmljYXRpb24gYnkgYSBMYW1iZGEgQXV0aG9yaXplciBvciBzaW1pbGFyIG1lY2hhbmlzbS5cbiAqL1xuZXhwb3J0IGNsYXNzIFdhZkh0dHBBcGkgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICAvKipcbiAgICogQHN0YXRpY1xuICAgKiBAcmVhZG9ubHlcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IFNFQ1JFVF9IRUFERVJfTkFNRVxuICAgKiBAZGVzY3JpcHRpb24gVGhlIG5hbWUgb2YgdGhlIGN1c3RvbSBoZWFkZXIgQ2xvdWRGcm9udCB3aWxsIGFkZCB0byByZXF1ZXN0c1xuICAgKiBmb3J3YXJkZWQgdG8gdGhlIG9yaWdpbi4gVGhpcyBoZWFkZXIgY2FuIGJlIHVzZWQgYnkgeW91ciBiYWNrZW5kIChlLmcuLFxuICAgKiBhIExhbWJkYSBBdXRob3JpemVyIGZvciBBUEkgR2F0ZXdheSkgdG8gdmVyaWZ5IHRoYXQgdGhlIHJlcXVlc3Qgb3JpZ2luYXRlZFxuICAgKiBmcm9tIENsb3VkRnJvbnQgYW5kIG5vdCBkaXJlY3RseSBmcm9tIHRoZSBpbnRlcm5ldC5cbiAgICovXG4gIHB1YmxpYyBzdGF0aWMgcmVhZG9ubHkgU0VDUkVUX0hFQURFUl9OQU1FID0gXCJYLU9yaWdpbi1WZXJpZnlcIjtcblxuICAvKipcbiAgICogQHJlYWRvbmx5XG4gICAqIEBwcm9wZXJ0eSB7Y2xvdWRmcm9udC5EaXN0cmlidXRpb259IGRpc3RyaWJ1dGlvblxuICAgKiBAZGVzY3JpcHRpb24gVGhlIENsb3VkRnJvbnQgZGlzdHJpYnV0aW9uIGNyZWF0ZWQgYW5kIG1hbmFnZWQgYnkgdGhpcyBjb25zdHJ1Y3QuXG4gICAqIFlvdSBjYW4gdXNlIHRoaXMgcHJvcGVydHkgdG8gcmV0cmlldmUgdGhlIGRpc3RyaWJ1dGlvbidzIGRvbWFpbiBuYW1lIG9yIEFSTi5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBkaXN0cmlidXRpb246IGNsb3VkZnJvbnQuRGlzdHJpYnV0aW9uO1xuXG4gIC8qKlxuICAgKiBAcmVhZG9ubHlcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IHNlY3JldEhlYWRlclZhbHVlXG4gICAqIEBkZXNjcmlwdGlvbiBUaGUgcmFuZG9tbHkgZ2VuZXJhdGVkIHNlY3JldCB2YWx1ZSBmb3IgdGhlIGN1c3RvbSBoZWFkZXIuXG4gICAqIFRoaXMgdmFsdWUgaXMgdW5pcXVlIGZvciBlYWNoIGRlcGxveW1lbnQgb2YgdGhlIGNvbnN0cnVjdC5cbiAgICogSXQgc2hvdWxkIGJlIHVzZWQgaW4geW91ciBIVFRQIEFQSSdzIGF1dGhvcml6ZXIgb3IgYmFja2VuZCBsb2dpY1xuICAgKiB0byB2YWxpZGF0ZSByZXF1ZXN0cyBjb21pbmcgdGhyb3VnaCBDbG91ZEZyb250LlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IHNlY3JldEhlYWRlclZhbHVlOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEBjb25zdHJ1Y3RvclxuICAgKiBAcGFyYW0ge0NvbnN0cnVjdH0gc2NvcGUgVGhlIHNjb3BlIGluIHdoaWNoIHRvIGRlZmluZSB0aGlzIGNvbnN0cnVjdCAoZS5nLiwgYSBDREsgU3RhY2spLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWQgVGhlIHVuaXF1ZSBpZGVudGlmaWVyIGZvciB0aGlzIGNvbnN0cnVjdCB3aXRoaW4gaXRzIHNjb3BlLlxuICAgKiBAcGFyYW0ge1dhZkh0dHBBcGlQcm9wc30gcHJvcHMgVGhlIHByb3BlcnRpZXMgcmVxdWlyZWQgdG8gY29uZmlndXJlIHRoaXMgY29uc3RydWN0LFxuICAgKiBpbmNsdWRpbmcgdGhlIHRhcmdldCBIVFRQIEFQSSBhbmQgb3B0aW9uYWwgV0FGIHJ1bGVzLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IFdhZkh0dHBBcGlQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICAvKipcbiAgICAgKiBAZXhhbXBsZVxuICAgICAqIC8vIEV4YW1wbGUgdXNhZ2Ugd2l0aGluIGEgQ0RLIFN0YWNrOlxuICAgICAqIGltcG9ydCB7IFN0YWNrLCBTdGFja1Byb3BzIH0gZnJvbSAnYXdzLWNkay1saWInO1xuICAgICAqIGltcG9ydCB7IEh0dHBBcGksIEh0dHBNZXRob2QsIEh0dHBJbnRlZ3JhdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1hcGlnYXRld2F5djInO1xuICAgICAqIGltcG9ydCB7IEh0dHBMYW1iZGFJbnRlZ3JhdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1hcGlnYXRld2F5djItaW50ZWdyYXRpb25zJztcbiAgICAgKiBpbXBvcnQgeyBOb2RlanNGdW5jdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1sYW1iZGEtbm9kZWpzJztcbiAgICAgKiBpbXBvcnQgeyBSdW50aW1lIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG4gICAgICogaW1wb3J0IHsgV2FmSHR0cEFwaSB9IGZyb20gJy4vd2FmLWh0dHAtYXBpJzsgLy8gQXNzdW1pbmcgeW91ciBjb25zdHJ1Y3QgaXMgaW4gdGhpcyBwYXRoXG4gICAgICpcbiAgICAgKiAvLyBEZWZpbmUgYSBzaW1wbGUgTGFtYmRhIGZ1bmN0aW9uIHRvIGludGVncmF0ZSB3aXRoIHRoZSBBUEkgR2F0ZXdheVxuICAgICAqIGNvbnN0IG15TGFtYmRhID0gbmV3IE5vZGVqc0Z1bmN0aW9uKHRoaXMsICdNeUFwaUhhbmRsZXInLCB7XG4gICAgICogcnVudGltZTogUnVudGltZS5OT0RFSlNfMThfWCxcbiAgICAgKiBoYW5kbGVyOiAnaGFuZGxlcicsXG4gICAgICogZW50cnk6ICdwYXRoL3RvL3lvdXIvbGFtYmRhL2NvZGUudHMnLCAvLyBTcGVjaWZ5IHRoZSBwYXRoIHRvIHlvdXIgTGFtYmRhIGZ1bmN0aW9uIGNvZGVcbiAgICAgKiB9KTtcbiAgICAgKlxuICAgICAqIC8vIENyZWF0ZSBhbiBIVFRQIEFQSSBHYXRld2F5XG4gICAgICogY29uc3QgaHR0cEFwaSA9IG5ldyBIdHRwQXBpKHRoaXMsICdNeUh0dHBBcGknLCB7XG4gICAgICogZGVzY3JpcHRpb246ICdNeSBleGFtcGxlIEhUVFAgQVBJJyxcbiAgICAgKiB9KTtcbiAgICAgKlxuICAgICAqIC8vIEFkZCBhIHJvdXRlIHRvIHRoZSBIVFRQIEFQSSB0aGF0IGludGVncmF0ZXMgd2l0aCB0aGUgTGFtYmRhIGZ1bmN0aW9uXG4gICAgICogaHR0cEFwaS5hZGRSb3V0ZXMoe1xuICAgICAqIHBhdGg6ICcvaGVsbG8nLFxuICAgICAqIG1ldGhvZHM6IFtIdHRwTWV0aG9kLkdFVF0sXG4gICAgICogaW50ZWdyYXRpb246IG5ldyBIdHRwTGFtYmRhSW50ZWdyYXRpb24oJ015TGFtYmRhSW50ZWdyYXRpb24nLCBteUxhbWJkYSksXG4gICAgICogfSk7XG4gICAgICpcbiAgICAgKiAvLyBJbnN0YW50aWF0ZSB0aGUgV2FmSHR0cEFwaSBjb25zdHJ1Y3QgdG8gcHJvdGVjdCB0aGUgSFRUUCBBUElcbiAgICAgKiBjb25zdCBwcm90ZWN0ZWRBcGkgPSBuZXcgV2FmSHR0cEFwaSh0aGlzLCAnUHJvdGVjdGVkTXlBcGknLCB7XG4gICAgICogaHR0cEFwaTogaHR0cEFwaSxcbiAgICAgKiAvLyBPcHRpb25hbGx5LCBwcm92aWRlIGN1c3RvbSBXQUYgcnVsZXM6XG4gICAgICogLy8gd2FmUnVsZXM6IFtcbiAgICAgKiAvLyAgIHtcbiAgICAgKiAvLyAgICAgbmFtZTogJ015Q3VzdG9tSXBCbG9ja1J1bGUnLFxuICAgICAqIC8vICAgICBwcmlvcml0eTogMTAsXG4gICAgICogLy8gICAgIHN0YXRlbWVudDoge1xuICAgICAqIC8vICAgICAgIGlwU2V0UmVmZXJlbmNlU3RhdGVtZW50OiB7IGFybjogJ2Fybjphd3M6d2FmdjI6Li4uJyB9XG4gICAgICogLy8gICAgIH0sXG4gICAgICogLy8gICAgIGFjdGlvbjogeyBibG9jazoge30gfSxcbiAgICAgKiAvLyAgICAgdmlzaWJpbGl0eUNvbmZpZzoge1xuICAgICAqIC8vICAgICAgIGNsb3VkV2F0Y2hNZXRyaWNzRW5hYmxlZDogdHJ1ZSxcbiAgICAgKiAvLyAgICAgICBtZXRyaWNOYW1lOiAnTXlDdXN0b21JcEJsb2NrJyxcbiAgICAgKiAvLyAgICAgICBzYW1wbGVkUmVxdWVzdHNFbmFibGVkOiB0cnVlLFxuICAgICAqIC8vICAgICB9LFxuICAgICAqIC8vICAgfSxcbiAgICAgKiAvLyBdLFxuICAgICAqIH0pO1xuICAgICAqXG4gICAgICogLy8gT3V0cHV0IHRoZSBDbG91ZEZyb250IFVSTCBvZiB0aGUgcHJvdGVjdGVkIEFQSVxuICAgICAqIG5ldyBjZGsuQ2ZuT3V0cHV0KHRoaXMsICdQcm90ZWN0ZWRBcGlFbmRwb2ludCcsIHtcbiAgICAgKiB2YWx1ZTogcHJvdGVjdGVkQXBpLmRpc3RyaWJ1dGlvbi5kaXN0cmlidXRpb25Eb21haW5OYW1lLFxuICAgICAqIGRlc2NyaXB0aW9uOiAnVGhlIENsb3VkRnJvbnQgVVJMIGZvciB0aGUgcHJvdGVjdGVkIEFQSSBlbmRwb2ludCcsXG4gICAgICogfSk7XG4gICAgICpcbiAgICAgKiAvLyBPdXRwdXQgdGhlIHNlY3JldCBoZWFkZXIgdmFsdWUgZm9yIHVzZSBpbiBhIExhbWJkYSBhdXRob3JpemVyIGlmIG5lZWRlZFxuICAgICAqIG5ldyBjZGsuQ2ZuT3V0cHV0KHRoaXMsICdPcmlnaW5WZXJpZmljYXRpb25TZWNyZXQnLCB7XG4gICAgICogdmFsdWU6IHByb3RlY3RlZEFwaS5zZWNyZXRIZWFkZXJWYWx1ZSxcbiAgICAgKiBkZXNjcmlwdGlvbjogJ1NlY3JldCB2YWx1ZSB0byB2ZXJpZnkgQ2xvdWRGcm9udCBvcmlnaW4gcmVxdWVzdHMnLFxuICAgICAqIH0pO1xuICAgICAqL1xuXG4gICAgLy8gR2VuZXJhdGUgYSBjcnlwdG9ncmFwaGljYWxseSBzdHJvbmcgcmFuZG9tIGhleCBzdHJpbmcgZm9yIHRoZSBzZWNyZXQgaGVhZGVyIHZhbHVlLlxuICAgIC8vIFRoaXMgZW5zdXJlcyB0aGF0IHRoZSBzZWNyZXQgaXMgdW5pcXVlIGFuZCBkaWZmaWN1bHQgdG8gZ3Vlc3MsIGVuaGFuY2luZyBzZWN1cml0eVxuICAgIC8vIHdoZW4gdXNlZCBmb3Igb3JpZ2luIHZlcmlmaWNhdGlvbi5cbiAgICB0aGlzLnNlY3JldEhlYWRlclZhbHVlID0gY3J5cHRvLnJhbmRvbUJ5dGVzKDE2KS50b1N0cmluZyhcImhleFwiKTtcblxuICAgIC8vIERldGVybWluZSB3aGljaCBXQUYgcnVsZXMgdG8gYXBwbHkuIElmIGN1c3RvbSBydWxlcyBhcmUgcHJvdmlkZWQgdmlhIHByb3BzLCB1c2UgdGhlbS5cbiAgICAvLyBPdGhlcndpc2UsIGZhbGwgYmFjayB0byB0aGUgZGVmYXVsdCBtYW5hZ2VkIHJ1bGVzIGRlZmluZWQgaW4gYGNyZWF0ZURlZmF1bHRSdWxlcygpYC5cbiAgICBjb25zdCBydWxlcyA9IHByb3BzLndhZlJ1bGVzID8/IHRoaXMuY3JlYXRlRGVmYXVsdFJ1bGVzKCk7XG5cbiAgICAvLyAxLiBDcmVhdGUgdGhlIEFXUyBXQUYgV2ViQUNMIChXZWIgQWNjZXNzIENvbnRyb2wgTGlzdClcbiAgICAvLyBUaGlzIFdlYkFDTCB3aWxsIGJlIGFzc29jaWF0ZWQgd2l0aCB0aGUgQ2xvdWRGcm9udCBkaXN0cmlidXRpb24gdG8gZmlsdGVyIHdlYiB0cmFmZmljLlxuICAgIGNvbnN0IHdlYkFjbCA9IG5ldyB3YWZ2Mi5DZm5XZWJBQ0wodGhpcywgaWQgKyBcIldlYkFjbFwiLCB7XG4gICAgICAvLyBEZWZhdWx0IGFjdGlvbiBmb3IgcmVxdWVzdHMgdGhhdCBkb24ndCBtYXRjaCBhbnkgcnVsZXMuICdhbGxvdycgbWVhbnMgdGhleSBwYXNzIHRocm91Z2guXG4gICAgICBkZWZhdWx0QWN0aW9uOiB7IGFsbG93OiB7fSB9LFxuICAgICAgLy8gVGhlIHNjb3BlIE1VU1QgYmUgJ0NMT1VERlJPTlQnIGZvciBhIFdlYkFDTCB0byBiZSBhc3NvY2lhdGVkIHdpdGggYSBDbG91ZEZyb250IGRpc3RyaWJ1dGlvbi5cbiAgICAgIHNjb3BlOiBcIkNMT1VERlJPTlRcIixcbiAgICAgIC8vIENvbmZpZ3VyYXRpb24gZm9yIENsb3VkV2F0Y2ggbWV0cmljcyBhbmQgc2FtcGxlZCByZXF1ZXN0cywgdXNlZnVsIGZvciBtb25pdG9yaW5nIFdBRiBhY3Rpdml0eS5cbiAgICAgIHZpc2liaWxpdHlDb25maWc6IHtcbiAgICAgICAgY2xvdWRXYXRjaE1ldHJpY3NFbmFibGVkOiB0cnVlLCAvLyBFbmFibGUgbWV0cmljcyB0byB2aWV3IFdBRiBwZXJmb3JtYW5jZSBpbiBDbG91ZFdhdGNoLlxuICAgICAgICBtZXRyaWNOYW1lOiBgJHtpZH0tV2FmYCwgLy8gVW5pcXVlIG1ldHJpYyBuYW1lIGZvciB0aGlzIFdlYkFDTC5cbiAgICAgICAgc2FtcGxlZFJlcXVlc3RzRW5hYmxlZDogdHJ1ZSwgLy8gRW5hYmxlIHNhbXBsaW5nIG9mIHJlcXVlc3RzIGZvciBkZWJ1Z2dpbmcgYW5kIGFuYWx5c2lzLlxuICAgICAgfSxcbiAgICAgIC8vIFRoZSBhcnJheSBvZiBydWxlcyB0aGF0IGRlZmluZSBob3cgdG8gaW5zcGVjdCBhbmQgaGFuZGxlIHdlYiByZXF1ZXN0cy5cbiAgICAgIHJ1bGVzOiBydWxlcyxcbiAgICB9KTtcblxuICAgIC8vIDIuIENyZWF0ZSB0aGUgQ2xvdWRGcm9udCBkaXN0cmlidXRpb25cbiAgICAvLyBUaGlzIGRpc3RyaWJ1dGlvbiBhY3RzIGFzIHRoZSBwdWJsaWMtZmFjaW5nIGVuZHBvaW50IGZvciB0aGUgSFRUUCBBUEksXG4gICAgLy8gcHJvdmlkaW5nIENETiBiZW5lZml0cyBsaWtlIGNhY2hpbmcgYW5kIHJlZHVjZWQgbGF0ZW5jeSwgYW5kIGludGVncmF0aW5nIHdpdGggV0FGLlxuICAgIHRoaXMuZGlzdHJpYnV0aW9uID0gbmV3IGNsb3VkZnJvbnQuRGlzdHJpYnV0aW9uKFxuICAgICAgdGhpcyxcbiAgICAgIGlkICsgXCJBcGlEaXN0cmlidXRpb25cIixcbiAgICAgIHtcbiAgICAgICAgY29tbWVudDogYENsb3VkRnJvbnQgZGlzdHJpYnV0aW9uIGZvciBIVFRQIEFQSTogJHtwcm9wcy5odHRwQXBpLmh0dHBBcGlJZH1gLCAvLyBEZXNjcmlwdGl2ZSBjb21tZW50IGZvciB0aGUgZGlzdHJpYnV0aW9uLlxuICAgICAgICAvLyBBc3NvY2lhdGUgdGhlIGNyZWF0ZWQgV0FGIFdlYkFDTCB3aXRoIHRoaXMgQ2xvdWRGcm9udCBkaXN0cmlidXRpb24uXG4gICAgICAgIHdlYkFjbElkOiB3ZWJBY2wuYXR0ckFybixcbiAgICAgICAgLy8gRW5mb3JjZSBhIG1pbmltdW0gVExTIHNlY3VyaXR5IHBvbGljeSBmb3IgY29ubmVjdGlvbnMgYmV0d2VlbiB2aWV3ZXJzIGFuZCBDbG91ZEZyb250LlxuICAgICAgICBtaW5pbXVtUHJvdG9jb2xWZXJzaW9uOiBjbG91ZGZyb250LlNlY3VyaXR5UG9saWN5UHJvdG9jb2wuVExTX1YxXzJfMjAyMSxcbiAgICAgICAgLy8gRGVmaW5lIHRoZSBkZWZhdWx0IGJlaGF2aW9yIGZvciByZXF1ZXN0cyBoaXR0aW5nIHRoaXMgZGlzdHJpYnV0aW9uLlxuICAgICAgICBkZWZhdWx0QmVoYXZpb3I6IHtcbiAgICAgICAgICAvLyBDb25maWd1cmUgdGhlIG9yaWdpbiAoYmFja2VuZCBzZXJ2ZXIpIGZvciB0aGUgZGlzdHJpYnV0aW9uLlxuICAgICAgICAgIC8vIGBGbi5zZWxlY3QoMiwgRm4uc3BsaXQoXCIvXCIsIHByb3BzLmh0dHBBcGkudXJsISkpYCBleHRyYWN0cyB0aGUgZG9tYWluIG5hbWVcbiAgICAgICAgICAvLyBmcm9tIHRoZSBIVFRQIEFQSSdzIFVSTCAoZS5nLiwgXCJhYmNkZWYuZXhlY3V0ZS1hcGkudXMtZWFzdC0xLmFtYXpvbmF3cy5jb21cIikuXG4gICAgICAgICAgb3JpZ2luOiBuZXcgb3JpZ2lucy5IdHRwT3JpZ2luKFxuICAgICAgICAgICAgRm4uc2VsZWN0KDIsIEZuLnNwbGl0KFwiL1wiLCBwcm9wcy5odHRwQXBpLnVybCEpKSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgLy8gRW5mb3JjZSBIVFRQUy1vbmx5IGNvbW11bmljYXRpb24gYmV0d2VlbiBDbG91ZEZyb250IGFuZCB0aGUgb3JpZ2luLlxuICAgICAgICAgICAgICBwcm90b2NvbFBvbGljeTogY2xvdWRmcm9udC5PcmlnaW5Qcm90b2NvbFBvbGljeS5IVFRQU19PTkxZLFxuICAgICAgICAgICAgICAvLyBBZGQgdGhlIGN1c3RvbSBzZWNyZXQgaGVhZGVyIHRvIGV2ZXJ5IHJlcXVlc3QgQ2xvdWRGcm9udCBmb3J3YXJkcyB0byB0aGUgb3JpZ2luLlxuICAgICAgICAgICAgICAvLyBUaGlzIGlzIGNydWNpYWwgZm9yIG9yaWdpbiB2ZXJpZmljYXRpb24uXG4gICAgICAgICAgICAgIGN1c3RvbUhlYWRlcnM6IHtcbiAgICAgICAgICAgICAgICBbV2FmSHR0cEFwaS5TRUNSRVRfSEVBREVSX05BTUVdOiB0aGlzLnNlY3JldEhlYWRlclZhbHVlLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICApLFxuICAgICAgICAgIC8vIFJlZGlyZWN0IGFsbCBIVFRQIHZpZXdlciByZXF1ZXN0cyB0byBIVFRQUyB0byBlbnN1cmUgc2VjdXJlIGNvbW11bmljYXRpb24uXG4gICAgICAgICAgdmlld2VyUHJvdG9jb2xQb2xpY3k6XG4gICAgICAgICAgICBjbG91ZGZyb250LlZpZXdlclByb3RvY29sUG9saWN5LlJFRElSRUNUX1RPX0hUVFBTLFxuICAgICAgICAgIC8vIEFsbG93IGFsbCBIVFRQIG1ldGhvZHMgKEdFVCwgUE9TVCwgUFVULCBERUxFVEUsIGV0Yy4pIHRvIGJlIGZvcndhcmRlZCB0byB0aGUgb3JpZ2luLlxuICAgICAgICAgIGFsbG93ZWRNZXRob2RzOiBjbG91ZGZyb250LkFsbG93ZWRNZXRob2RzLkFMTE9XX0FMTCxcbiAgICAgICAgICAvLyBEaXNhYmxlIGNhY2hpbmcgZm9yIEFQSSByZXF1ZXN0cyBieSBkZWZhdWx0LCBhcyBBUEkgcmVzcG9uc2VzIGFyZSB0eXBpY2FsbHkgZHluYW1pY1xuICAgICAgICAgIC8vIGFuZCBzaG91bGQgbm90IGJlIGNhY2hlZCBieSB0aGUgQ0ROLlxuICAgICAgICAgIGNhY2hlUG9saWN5OiBjbG91ZGZyb250LkNhY2hlUG9saWN5LkNBQ0hJTkdfRElTQUJMRUQsXG4gICAgICAgICAgLy8gRm9yd2FyZCBhbGwgdmlld2VyIGhlYWRlcnMsIHF1ZXJ5IHN0cmluZ3MsIGFuZCBjb29raWVzIHRvIHRoZSBvcmlnaW4sXG4gICAgICAgICAgLy8gRVhDRVBUIGZvciB0aGUgJ0hvc3QnIGhlYWRlci4gVGhlICdIb3N0JyBoZWFkZXIgbmVlZHMgdG8gYmUgdGhlIEFQSSBHYXRld2F5J3NcbiAgICAgICAgICAvLyBob3N0IHRvIGVuc3VyZSBjb3JyZWN0IHJvdXRpbmcgd2l0aGluIEFQSSBHYXRld2F5LlxuICAgICAgICAgIG9yaWdpblJlcXVlc3RQb2xpY3k6XG4gICAgICAgICAgICBjbG91ZGZyb250Lk9yaWdpblJlcXVlc3RQb2xpY3kuQUxMX1ZJRVdFUl9FWENFUFRfSE9TVF9IRUFERVIsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogQHByaXZhdGVcbiAgICogQG1ldGhvZCBjcmVhdGVEZWZhdWx0UnVsZXNcbiAgICogQGRlc2NyaXB0aW9uIENyZWF0ZXMgYSBkZWZhdWx0IHNldCBvZiBBV1MgTWFuYWdlZCBSdWxlcyBmb3IgdGhlIFdBRiBXZWJBQ0wuXG4gICAqIFRoZXNlIHJ1bGVzIHByb3ZpZGUgYSBnb29kIGJhc2VsaW5lIG9mIHByb3RlY3Rpb24gYWdhaW5zdCBjb21tb24gdGhyZWF0cy5cbiAgICogQHJldHVybnMge3dhZnYyLkNmbldlYkFDTC5SdWxlUHJvcGVydHlbXX0gQW4gYXJyYXkgb2YgV0FGIHJ1bGUgcHJvcGVydGllcy5cbiAgICovXG4gIHByaXZhdGUgY3JlYXRlRGVmYXVsdFJ1bGVzKCk6IHdhZnYyLkNmbldlYkFDTC5SdWxlUHJvcGVydHlbXSB7XG4gICAgcmV0dXJuIFtcbiAgICAgIHtcbiAgICAgICAgbmFtZTogXCJBV1MtQVdTTWFuYWdlZFJ1bGVzQW1hem9uSXBSZXB1dGF0aW9uTGlzdFwiLFxuICAgICAgICBwcmlvcml0eTogMSwgLy8gUnVsZXMgYXJlIGV2YWx1YXRlZCBpbiBvcmRlciBvZiBwcmlvcml0eSAobG93ZXIgbnVtYmVyIGZpcnN0KS5cbiAgICAgICAgc3RhdGVtZW50OiB7XG4gICAgICAgICAgLy8gU3BlY2lmaWVzIGEgbWFuYWdlZCBydWxlIGdyb3VwIHByb3ZpZGVkIGJ5IEFXUy5cbiAgICAgICAgICBtYW5hZ2VkUnVsZUdyb3VwU3RhdGVtZW50OiB7XG4gICAgICAgICAgICB2ZW5kb3JOYW1lOiBcIkFXU1wiLFxuICAgICAgICAgICAgbmFtZTogXCJBV1NNYW5hZ2VkUnVsZXNBbWF6b25JcFJlcHV0YXRpb25MaXN0XCIsIC8vIFRoaXMgcnVsZSBncm91cCBibG9ja3Mga25vd24gbWFsaWNpb3VzIElQIGFkZHJlc3Nlcy5cbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICAvLyBUaGUgYG5vbmVgIGFjdGlvbiBtZWFucyB0aGUgcnVsZSBncm91cCdzIGRlZmF1bHQgYWN0aW9uICh1c3VhbGx5ICdibG9jaycpIHdpbGwgYXBwbHkuXG4gICAgICAgIG92ZXJyaWRlQWN0aW9uOiB7IG5vbmU6IHt9IH0sXG4gICAgICAgIC8vIENvbmZpZ3VyYXRpb24gZm9yIENsb3VkV2F0Y2ggbWV0cmljcyBhbmQgc2FtcGxlZCByZXF1ZXN0cyBmb3IgdGhpcyBzcGVjaWZpYyBydWxlLlxuICAgICAgICB2aXNpYmlsaXR5Q29uZmlnOiB7XG4gICAgICAgICAgY2xvdWRXYXRjaE1ldHJpY3NFbmFibGVkOiB0cnVlLFxuICAgICAgICAgIG1ldHJpY05hbWU6IFwiYXdzTWFuYWdlZFJ1bGVzQW1hem9uSXBSZXB1dGF0aW9uTGlzdFwiLFxuICAgICAgICAgIHNhbXBsZWRSZXF1ZXN0c0VuYWJsZWQ6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBuYW1lOiBcIkFXUy1BV1NNYW5hZ2VkUnVsZXNDb21tb25SdWxlU2V0XCIsXG4gICAgICAgIHByaW9yaXR5OiAyLFxuICAgICAgICBzdGF0ZW1lbnQ6IHtcbiAgICAgICAgICBtYW5hZ2VkUnVsZUdyb3VwU3RhdGVtZW50OiB7XG4gICAgICAgICAgICB2ZW5kb3JOYW1lOiBcIkFXU1wiLFxuICAgICAgICAgICAgbmFtZTogXCJBV1NNYW5hZ2VkUnVsZXNDb21tb25SdWxlU2V0XCIsIC8vIFRoaXMgcnVsZSBncm91cCBwcm90ZWN0cyBhZ2FpbnN0IGEgYnJvYWQgcmFuZ2Ugb2YgY29tbW9uIHdlYiBleHBsb2l0cy5cbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICBvdmVycmlkZUFjdGlvbjogeyBub25lOiB7fSB9LFxuICAgICAgICB2aXNpYmlsaXR5Q29uZmlnOiB7XG4gICAgICAgICAgY2xvdWRXYXRjaE1ldHJpY3NFbmFibGVkOiB0cnVlLFxuICAgICAgICAgIG1ldHJpY05hbWU6IFwiYXdzTWFuYWdlZFJ1bGVzQ29tbW9uUnVsZVNldFwiLFxuICAgICAgICAgIHNhbXBsZWRSZXF1ZXN0c0VuYWJsZWQ6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIF07XG4gIH1cbn1cbiJdfQ==