From e9c9901c4f10a787b76ce1110769f79d7cc70e43 Mon Sep 17 00:00:00 2001 From: biffgaut Date: Tue, 4 Apr 2023 16:26:16 -0400 Subject: [PATCH] Allow any type for webAclProps --- .../aws-wafwebacl-apigateway/lib/index.ts | 2 +- .../integ.partial-arguments.expected.json | 696 ++++++++++++++++++ .../test/integ.partial-arguments.ts | 34 + .../test/test.wafwebacl-apigateway.test.ts | 29 +- .../aws-wafwebacl-appsync/lib/index.ts | 2 +- .../integ.partial-arguments.expected.json | 205 ++++++ .../test/integ.partial-arguments.ts | 38 + .../test/test.wafwebacl-appsync.test.ts | 20 +- .../aws-wafwebacl-cloudfront/lib/index.ts | 2 +- .../integ.partial-arguments.expected.json | 266 +++++++ .../test/integ.partial-arguments.ts | 34 + .../test/test.wafwebacl-cloudfront.test.ts | 30 +- .../core/lib/waf-helper.ts | 2 +- .../core/test/waf-helper.test.ts | 24 +- 14 files changed, 1344 insertions(+), 40 deletions(-) create mode 100644 source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.partial-arguments.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.partial-arguments.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/integ.partial-arguments.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/integ.partial-arguments.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/integ.partial-arguments.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/integ.partial-arguments.ts diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/lib/index.ts index a492fc189..e88e360e6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/lib/index.ts @@ -36,7 +36,7 @@ export interface WafwebaclToApiGatewayProps { * * @default - Default properties are used. */ - readonly webaclProps?: waf.CfnWebACLProps, + readonly webaclProps?: waf.CfnWebACLProps | any, } /** diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.partial-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.partial-arguments.expected.json new file mode 100644 index 000000000..408ac35b8 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.partial-arguments.expected.json @@ -0,0 +1,696 @@ +{ + "Resources": { + "testFunctionServiceRoleFEC29B6F": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "testFunction483F4CBE": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "0904d3723480fed2daf7885caa427b930881caae6879d1e6b0d395020173ef6f.zip" + }, + "Role": { + "Fn::GetAtt": [ + "testFunctionServiceRoleFEC29B6F", + "Arn" + ] + }, + "Handler": ".handler", + "Runtime": "nodejs14.x" + }, + "DependsOn": [ + "testFunctionServiceRoleFEC29B6F" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Test Resource" + }, + { + "id": "W89", + "reason": "Test Resource" + }, + { + "id": "W92", + "reason": "Test Resource" + } + ] + } + } + }, + "testApiD6ECAB50": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Name": "testApi" + } + }, + "testApiCloudWatchRole8A11639C": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" + ] + ] + } + ] + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testApiAccountA6723CBB": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "testApiCloudWatchRole8A11639C", + "Arn" + ] + } + }, + "DependsOn": [ + "testApiD6ECAB50" + ], + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testApiDeployment3727A0B98616f677c89f00f84016798f65d107c0": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "testApiD6ECAB50" + }, + "Description": "Automatically created by the RestApi construct" + }, + "DependsOn": [ + "testApiproxyANYC53F2608", + "testApiproxyA0E5503A", + "testApiANYDC600770" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W68", + "reason": "Test Resource" + } + ] + } + } + }, + "testApiDeploymentStageprodCE051BE8": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "testApiD6ECAB50" + }, + "DeploymentId": { + "Ref": "testApiDeployment3727A0B98616f677c89f00f84016798f65d107c0" + }, + "StageName": "prod" + }, + "DependsOn": [ + "testApiAccountA6723CBB" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W64", + "reason": "Test Resource" + }, + { + "id": "W69", + "reason": "Test Resource" + } + ] + } + } + }, + "testApiproxyA0E5503A": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Fn::GetAtt": [ + "testApiD6ECAB50", + "RootResourceId" + ] + }, + "PathPart": "{proxy+}", + "RestApiId": { + "Ref": "testApiD6ECAB50" + } + } + }, + "testApiproxyANYApiPermissionpartialargumentstestApi67E53489ANYproxyD442645B": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "testFunction483F4CBE", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testApiD6ECAB50" + }, + "/", + { + "Ref": "testApiDeploymentStageprodCE051BE8" + }, + "/*/*" + ] + ] + } + } + }, + "testApiproxyANYApiPermissionTestpartialargumentstestApi67E53489ANYproxyA0A33584": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "testFunction483F4CBE", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testApiD6ECAB50" + }, + "/test-invoke-stage/*/*" + ] + ] + } + } + }, + "testApiproxyANYC53F2608": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "ANY", + "ResourceId": { + "Ref": "testApiproxyA0E5503A" + }, + "RestApiId": { + "Ref": "testApiD6ECAB50" + }, + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "testFunction483F4CBE", + "Arn" + ] + }, + "/invocations" + ] + ] + } + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W59", + "reason": "Test Resource" + } + ] + } + } + }, + "testApiANYApiPermissionpartialargumentstestApi67E53489ANY0CE0FBBB": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "testFunction483F4CBE", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testApiD6ECAB50" + }, + "/", + { + "Ref": "testApiDeploymentStageprodCE051BE8" + }, + "/*/" + ] + ] + } + } + }, + "testApiANYApiPermissionTestpartialargumentstestApi67E53489ANYFA990832": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "testFunction483F4CBE", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "testApiD6ECAB50" + }, + "/test-invoke-stage/*/" + ] + ] + } + } + }, + "testApiANYDC600770": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "ANY", + "ResourceId": { + "Fn::GetAtt": [ + "testApiD6ECAB50", + "RootResourceId" + ] + }, + "RestApiId": { + "Ref": "testApiD6ECAB50" + }, + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "testFunction483F4CBE", + "Arn" + ] + }, + "/invocations" + ] + ] + } + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W59", + "reason": "Test Resource" + } + ] + } + } + }, + "testwafwebaclapigatewaylambdatestwafwebaclapigatewaylambdaWebACL9DE6948C": { + "Type": "AWS::WAFv2::WebACL", + "Properties": { + "DefaultAction": { + "Allow": {} + }, + "Scope": "REGIONAL", + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "webACL", + "SampledRequestsEnabled": true + }, + "Name": "test-name-apigateway", + "Rules": [ + { + "Name": "AWS-AWSManagedRulesBotControlRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 0, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesBotControlRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesBotControlRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesKnownBadInputsRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 1, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesKnownBadInputsRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesKnownBadInputsRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesCommonRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 2, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesCommonRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesCommonRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAnonymousIpList", + "OverrideAction": { + "None": {} + }, + "Priority": 3, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAnonymousIpList", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAnonymousIpList", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAmazonIpReputationList", + "OverrideAction": { + "None": {} + }, + "Priority": 4, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAmazonIpReputationList", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAmazonIpReputationList", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAdminProtectionRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 5, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAdminProtectionRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAdminProtectionRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesSQLiRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 6, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesSQLiRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesSQLiRuleSet", + "SampledRequestsEnabled": true + } + } + ] + } + }, + "testwafwebaclapigatewaylambdaWebACLAssociation": { + "Type": "AWS::WAFv2::WebACLAssociation", + "Properties": { + "ResourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + "::/restapis/", + { + "Ref": "testApiD6ECAB50" + }, + "/stages/", + { + "Ref": "testApiDeploymentStageprodCE051BE8" + } + ] + ] + }, + "WebACLArn": { + "Fn::GetAtt": [ + "testwafwebaclapigatewaylambdatestwafwebaclapigatewaylambdaWebACL9DE6948C", + "Arn" + ] + } + } + } + }, + "Outputs": { + "testApiEndpointC9D07ADA": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "testApiD6ECAB50" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "testApiDeploymentStageprodCE051BE8" + }, + "/" + ] + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.partial-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.partial-arguments.ts new file mode 100644 index 000000000..a94a553fb --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/integ.partial-arguments.ts @@ -0,0 +1,34 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/// !cdk-integ * +import { App, Stack } from "aws-cdk-lib"; +import { WafwebaclToApiGateway } from "../lib"; +import { generateIntegStackName } from "@aws-solutions-constructs/core"; +import { CreateTestApi } from './test-helper'; + +const app = new App(); + +// Empty arguments +const stack = new Stack(app, generateIntegStackName(__filename)); + +const restApi = CreateTestApi(stack, 'test'); + +new WafwebaclToApiGateway(stack, "test-wafwebacl-apigateway-lambda", { + existingApiGatewayInterface: restApi, + webaclProps: { + name: 'test-name-apigateway' + } +}); + +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/test.wafwebacl-apigateway.test.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/test.wafwebacl-apigateway.test.ts index d8fce9ae2..cc6da5575 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/test.wafwebacl-apigateway.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-apigateway/test/test.wafwebacl-apigateway.test.ts @@ -19,7 +19,7 @@ import * as waf from "aws-cdk-lib/aws-wafv2"; import * as defaults from '@aws-solutions-constructs/core'; import { Template } from 'aws-cdk-lib/assertions'; -function deployConstruct(stack: cdk.Stack, constructProps?: waf.CfnWebACLProps) { +function deployConstruct(stack: cdk.Stack, constructProps?: waf.CfnWebACLProps | any) { const restApi = new api.RestApi(stack, 'test-api', {}); restApi.root.addMethod('ANY'); @@ -30,9 +30,6 @@ function deployConstruct(stack: cdk.Stack, constructProps?: waf.CfnWebACLProps) return new WafwebaclToApiGateway(stack, 'test-wafwebacl-apigateway', props); } -// -------------------------------------------------------------- -// Test error handling for existing WAF web ACL and user provided web ACL props -// -------------------------------------------------------------- test('Test error handling for existing WAF web ACL and user provider web ACL props', () => { const stack = new cdk.Stack(); const props: waf.CfnWebACLProps = { @@ -59,9 +56,6 @@ test('Test error handling for existing WAF web ACL and user provider web ACL pro }).toThrowError(); }); -// -------------------------------------------------------------- -// Test default deployment -// -------------------------------------------------------------- test('Test default deployment', () => { const stack = new cdk.Stack(); const construct = deployConstruct(stack); @@ -202,9 +196,6 @@ test('Test default deployment', () => { }); }); -// -------------------------------------------------------------- -// Test web acl with user provided acl props -// -------------------------------------------------------------- test('Test user provided acl props', () => { const stack = new cdk.Stack(); const webaclProps: waf.CfnWebACLProps = { @@ -273,9 +264,21 @@ test('Test user provided acl props', () => { }); }); -// -------------------------------------------------------------- -// Test existing web ACL -// -------------------------------------------------------------- +test('Test user provided partial acl props', () => { + const stack = new cdk.Stack(); + const testName = 'test-name'; + const webaclProps = { + name: testName + }; + + deployConstruct(stack, webaclProps); + + const template = Template.fromStack(stack); + template.hasResourceProperties("AWS::WAFv2::WebACL", { + Name: testName + }); +}); + test('Test existing web ACL', () => { const stack = new cdk.Stack(); const webacl: waf.CfnWebACL = new waf.CfnWebACL(stack, 'test-webacl', { diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/lib/index.ts index d75d2e01a..4dcf3d035 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/lib/index.ts @@ -35,7 +35,7 @@ export interface WafwebaclToAppsyncProps { * * @default - Default properties are used. */ - readonly webaclProps?: waf.CfnWebACLProps; + readonly webaclProps?: waf.CfnWebACLProps | any; } /** diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/integ.partial-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/integ.partial-arguments.expected.json new file mode 100644 index 000000000..c7f333c0c --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/integ.partial-arguments.expected.json @@ -0,0 +1,205 @@ +{ + "Resources": { + "newgraphqlapi": { + "Type": "AWS::AppSync::GraphQLApi", + "Properties": { + "AuthenticationType": "API_KEY", + "Name": "api" + } + }, + "testwafwebaclappsynctestwafwebaclappsyncWebACLA64C38D0": { + "Type": "AWS::WAFv2::WebACL", + "Properties": { + "DefaultAction": { + "Allow": {} + }, + "Scope": "REGIONAL", + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "webACL", + "SampledRequestsEnabled": true + }, + "Name": "test-name-appsync", + "Rules": [ + { + "Name": "AWS-AWSManagedRulesBotControlRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 0, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesBotControlRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesBotControlRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesKnownBadInputsRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 1, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesKnownBadInputsRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesKnownBadInputsRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesCommonRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 2, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesCommonRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesCommonRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAnonymousIpList", + "OverrideAction": { + "None": {} + }, + "Priority": 3, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAnonymousIpList", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAnonymousIpList", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAmazonIpReputationList", + "OverrideAction": { + "None": {} + }, + "Priority": 4, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAmazonIpReputationList", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAmazonIpReputationList", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAdminProtectionRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 5, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAdminProtectionRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAdminProtectionRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesSQLiRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 6, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesSQLiRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesSQLiRuleSet", + "SampledRequestsEnabled": true + } + } + ] + } + }, + "testwafwebaclappsyncWebACLAssociation": { + "Type": "AWS::WAFv2::WebACLAssociation", + "Properties": { + "ResourceArn": { + "Fn::GetAtt": [ + "newgraphqlapi", + "Arn" + ] + }, + "WebACLArn": { + "Fn::GetAtt": [ + "testwafwebaclappsynctestwafwebaclappsyncWebACLA64C38D0", + "Arn" + ] + } + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/integ.partial-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/integ.partial-arguments.ts new file mode 100644 index 000000000..cd8789717 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/integ.partial-arguments.ts @@ -0,0 +1,38 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/// !cdk-integ * +import { App, Stack } from "aws-cdk-lib"; +import { WafwebaclToAppsync } from "../lib"; +import { generateIntegStackName } from "@aws-solutions-constructs/core"; +import * as appsync from "aws-cdk-lib/aws-appsync"; + +const app = new App(); + +// Empty arguments +const stack = new Stack(app, generateIntegStackName(__filename)); + +const api = new appsync.CfnGraphQLApi(stack, "new-graphql-api", { + name: "api", + authenticationType: "API_KEY", +}); + +// This construct can only be attached to a configured Appsync API. +new WafwebaclToAppsync(stack, "test-wafwebacl-appsync", { + existingAppsyncApi: api, + webaclProps: { + name: 'test-name-appsync' + } +}); + +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/test.wafwebacl-appsync.test.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/test.wafwebacl-appsync.test.ts index 66150c224..6b13384b6 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/test.wafwebacl-appsync.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-appsync/test/test.wafwebacl-appsync.test.ts @@ -28,7 +28,7 @@ function deployAppsyncGraphqlApi(stack: cdk.Stack) { function deployConstruct( stack: cdk.Stack, - webaclProps?: waf.CfnWebACLProps, + webaclProps?: waf.CfnWebACLProps | any, existingWebaclObj?: waf.CfnWebACL ) { const api = deployAppsyncGraphqlApi(stack); @@ -212,9 +212,6 @@ test("Test default deployment", () => { }); }); -// -------------------------------------------------------------- -// Test web acl with user provided acl props -// -------------------------------------------------------------- test("Test user provided acl props", () => { const stack = new cdk.Stack(); const webaclProps: waf.CfnWebACLProps = { @@ -283,6 +280,21 @@ test("Test user provided acl props", () => { }); }); +test("Test user provided partial acl props", () => { + const stack = new cdk.Stack(); + const testName = 'test-name'; + const webaclProps = { + name: testName + }; + + deployConstruct(stack, webaclProps); + + const template = Template.fromStack(stack); + template.hasResourceProperties("AWS::WAFv2::WebACL", { + Name: testName + }); +}); + // -------------------------------------------------------------- // Test existing web ACL // -------------------------------------------------------------- diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/lib/index.ts index 1b84a0189..757996dd5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/lib/index.ts @@ -39,7 +39,7 @@ export interface WafwebaclToCloudFrontProps { * * @default - Default properties are used. */ - readonly webaclProps?: waf.CfnWebACLProps, + readonly webaclProps?: waf.CfnWebACLProps | any, } /** diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/integ.partial-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/integ.partial-arguments.expected.json new file mode 100644 index 000000000..d90fd31af --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/integ.partial-arguments.expected.json @@ -0,0 +1,266 @@ +{ + "Resources": { + "distro40218A07": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "partialargumentsdistroOriginGroup13A14546B", + "ViewerProtocolPolicy": "allow-all" + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "OriginGroups": { + "Items": [ + { + "FailoverCriteria": { + "StatusCodes": { + "Items": [ + 404 + ], + "Quantity": 1 + } + }, + "Id": "partialargumentsdistroOriginGroup13A14546B", + "Members": { + "Items": [ + { + "OriginId": "partialargumentsdistroOrigin1D2A7BC52" + }, + { + "OriginId": "partialargumentsdistroOrigin2689E5A52" + } + ], + "Quantity": 2 + } + } + ], + "Quantity": 1 + }, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only", + "OriginSSLProtocols": [ + "TLSv1.2" + ] + }, + "DomainName": "www.example.com", + "Id": "partialargumentsdistroOrigin1D2A7BC52" + }, + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only", + "OriginSSLProtocols": [ + "TLSv1.2" + ] + }, + "DomainName": "admin.example.com", + "Id": "partialargumentsdistroOrigin2689E5A52" + } + ], + "WebACLId": { + "Fn::GetAtt": [ + "testwafwebaclcloudfronts3testwafwebaclcloudfronts3WebACLC6A34449", + "Arn" + ] + } + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W10", + "reason": "Test Resource" + }, + { + "id": "W70", + "reason": "Test Resource" + } + ] + } + } + }, + "testwafwebaclcloudfronts3testwafwebaclcloudfronts3WebACLC6A34449": { + "Type": "AWS::WAFv2::WebACL", + "Properties": { + "DefaultAction": { + "Allow": {} + }, + "Scope": "CLOUDFRONT", + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "webACL", + "SampledRequestsEnabled": true + }, + "Name": "test-name-cloudfront", + "Rules": [ + { + "Name": "AWS-AWSManagedRulesBotControlRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 0, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesBotControlRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesBotControlRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesKnownBadInputsRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 1, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesKnownBadInputsRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesKnownBadInputsRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesCommonRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 2, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesCommonRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesCommonRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAnonymousIpList", + "OverrideAction": { + "None": {} + }, + "Priority": 3, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAnonymousIpList", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAnonymousIpList", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAmazonIpReputationList", + "OverrideAction": { + "None": {} + }, + "Priority": 4, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAmazonIpReputationList", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAmazonIpReputationList", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesAdminProtectionRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 5, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesAdminProtectionRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesAdminProtectionRuleSet", + "SampledRequestsEnabled": true + } + }, + { + "Name": "AWS-AWSManagedRulesSQLiRuleSet", + "OverrideAction": { + "None": {} + }, + "Priority": 6, + "Statement": { + "ManagedRuleGroupStatement": { + "Name": "AWSManagedRulesSQLiRuleSet", + "VendorName": "AWS" + } + }, + "VisibilityConfig": { + "CloudWatchMetricsEnabled": true, + "MetricName": "AWSManagedRulesSQLiRuleSet", + "SampledRequestsEnabled": true + } + } + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/integ.partial-arguments.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/integ.partial-arguments.ts new file mode 100644 index 000000000..20c2c6046 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/integ.partial-arguments.ts @@ -0,0 +1,34 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/// !cdk-integ * +import { App, Stack } from "aws-cdk-lib"; +import { WafwebaclToCloudFront } from "../lib"; +import { generateIntegStackName } from '@aws-solutions-constructs/core'; +import { CreateTestDistro } from './test-helper'; + +const app = new App(); + +// Empty arguments +const stack = new Stack(app, generateIntegStackName(__filename)); + +const newDistro = CreateTestDistro(stack, "distro"); + +new WafwebaclToCloudFront(stack, 'test-wafwebacl-cloudfront-s3', { + webaclProps: { + name: 'test-name-cloudfront' + }, + existingCloudFrontWebDistribution: newDistro, +}); + +app.synth(); \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/test.wafwebacl-cloudfront.test.ts b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/test.wafwebacl-cloudfront.test.ts index c8e7f3bd4..77461d1d4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/test.wafwebacl-cloudfront.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-wafwebacl-cloudfront/test/test.wafwebacl-cloudfront.test.ts @@ -21,7 +21,7 @@ import * as waf from "aws-cdk-lib/aws-wafv2"; import * as defaults from '@aws-solutions-constructs/core'; import { Template } from 'aws-cdk-lib/assertions'; -function deployConstruct(stack: cdk.Stack, constructProps?: waf.CfnWebACLProps) { +function deployConstruct(stack: cdk.Stack, constructProps?: waf.CfnWebACLProps | any) { const myBucket = new s3.Bucket(stack, 'myBucket', { removalPolicy: cdk.RemovalPolicy.DESTROY }); @@ -37,9 +37,6 @@ function deployConstruct(stack: cdk.Stack, constructProps?: waf.CfnWebACLProps) return new WafwebaclToCloudFront(stack, 'test-wafwebacl-cloudfront', props); } -// -------------------------------------------------------------- -// Test error handling for existing WAF web ACL and user provided web ACL props -// -------------------------------------------------------------- test('Test error handling for existing WAF web ACL and user provider web ACL props', () => { const stack = new cdk.Stack(); const props: waf.CfnWebACLProps = { @@ -69,9 +66,6 @@ test('Test error handling for existing WAF web ACL and user provider web ACL pro }).toThrowError(); }); -// -------------------------------------------------------------- -// Test default deployment -// -------------------------------------------------------------- test('Test default deployment', () => { const stack = new cdk.Stack(); const construct = deployConstruct(stack); @@ -212,9 +206,6 @@ test('Test default deployment', () => { }); }); -// -------------------------------------------------------------- -// Test web acl with user provided acl props -// -------------------------------------------------------------- test('Test user provided acl props', () => { const stack = new cdk.Stack(); const webaclProps: waf.CfnWebACLProps = { @@ -283,9 +274,22 @@ test('Test user provided acl props', () => { }); }); -// -------------------------------------------------------------- -// Test existing web ACL -// -------------------------------------------------------------- +test('Test user provided partial acl props', () => { + const stack = new cdk.Stack(); + const testName = 'test-name'; + + const webaclProps = { + name: testName + }; + + deployConstruct(stack, webaclProps); + + const template = Template.fromStack(stack); + template.hasResourceProperties("AWS::WAFv2::WebACL", { + Name: testName + }); +}); + test('Test existing web ACL', () => { const stack = new cdk.Stack(); const webacl: waf.CfnWebACL = new waf.CfnWebACL(stack, 'test-webacl', { diff --git a/source/patterns/@aws-solutions-constructs/core/lib/waf-helper.ts b/source/patterns/@aws-solutions-constructs/core/lib/waf-helper.ts index 4e34f1a52..3f4b51c97 100644 --- a/source/patterns/@aws-solutions-constructs/core/lib/waf-helper.ts +++ b/source/patterns/@aws-solutions-constructs/core/lib/waf-helper.ts @@ -29,7 +29,7 @@ export interface BuildWebaclProps { /** * User provided props to override the default ACL props for WAF web ACL. */ - readonly webaclProps?: waf.CfnWebACLProps; + readonly webaclProps?: waf.CfnWebACLProps | any; } /** diff --git a/source/patterns/@aws-solutions-constructs/core/test/waf-helper.test.ts b/source/patterns/@aws-solutions-constructs/core/test/waf-helper.test.ts index a99f442fc..d93ba3d19 100644 --- a/source/patterns/@aws-solutions-constructs/core/test/waf-helper.test.ts +++ b/source/patterns/@aws-solutions-constructs/core/test/waf-helper.test.ts @@ -17,9 +17,6 @@ import * as defaults from '..'; import { Template } from 'aws-cdk-lib/assertions'; import { buildWebacl } from '..'; -// -------------------------------------------------------------- -// Test construct with default props -// -------------------------------------------------------------- test('Test construct with default props', () => { // Stack const stack = new Stack(); @@ -168,9 +165,6 @@ test('Test construct with default props', () => { template.resourceCountIs('AWS::WAFv2::WebACLAssociation', 0); }); -// -------------------------------------------------------------- -// Test deployment w/ user provided custom properties -// -------------------------------------------------------------- test('Test deployment w/ user provided custom properties', () => { // Stack const stack = new Stack(); @@ -244,6 +238,24 @@ test('Test deployment w/ user provided custom properties', () => { }); }); +test('Test deployment w/ user provided partial custom properties', () => { + // Stack + const stack = new Stack(); + const testName = 'test-name'; + // Build WAF web ACL + const props = { + name: testName + }; + + defaults.buildWebacl(stack, 'CLOUDFRONT', { + webaclProps: props + }); + + Template.fromStack(stack).hasResourceProperties("AWS::WAFv2::WebACL", { + Name: testName + }); +}); + // -------------------------------------------------------------- // Test deployment w/ existing WAF web ACL provided // --------------------------------------------------------------