Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to version v2.0.1 #133

Merged
merged 1 commit into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@ source/infrastructure/test/mock-lambda-func/java-lambda/checkstyle-result.xml
dist/
*.log
bak.*
*.bak
*.ipynb*
_bandit_temp.json
RapidAi.code-workspace
source/scripts/v2_migration/poetry.lock
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.1] - 2024-08-19

### Changed

- With the release of [AWS-Solutions-Constructs v2.65.0](https://github.com/awslabs/aws-solutions-constructs/tree/main/source/patterns/%40aws-solutions-constructs/aws-apigatewayv2websocket-sqs), the AWS ApiGateway websocket integration with Amazon SQS Queue is available in the library. Hence the
implementation has been updated to use this construct.

### Fixed

- Issue [#131](https://github.com/aws-solutions/generative-ai-application-builder-on-aws/issues/131) which caused an issue with rendering non-S3 source URLs from vector store for a RAG based use case.
- Issue [#132](https://github.com/aws-solutions/generative-ai-application-builder-on-aws/issues/132) where configured Guardrails for Amazon Bedrock to have no effect on a use case's text input validations and output responses.
- Wizard validation failure for SageMaker model selection type, that allowed user to navigate ahead even when the page had failed validations.
- An AWS WAF rule that blocked larger payloads for HTTP POST request to the `/deployments` endpoint. This restricted configuring large system prompts (over 8 KB) for use case cases.

## [2.0.0] - 2024-08-08

### Added
Expand Down
1 change: 1 addition & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ aws-lambda-powertools==2.20.0 MIT
aws-sdk-client-mock MIT
aws-sdk-mock Apache-2.0
aws-xray-sdk==2.12.0 Apache-2.0
axios MIT
black MIT
boolean.py BSD-2-Clause
bootstrap MIT
Expand Down
3 changes: 3 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Reporting Security Issues
----------------------------------------------------------------------------------------------------------
We take all security reports seriously. When we receive such reports, we will investigate and subsequently address any potential vulnerabilities as quickly as possible. If you discover a potential security issue in this project, please notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to [AWS Security](mailto:[email protected]). Please do not create a public GitHub issue in this project.
2 changes: 1 addition & 1 deletion deployment/build-s3-dist.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ set -e
# Check to see if input has been provided:
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]; then
echo "Please provide all required parameters for the build script"
echo "For example: ./build-s3-dist.sh solutions trademarked-solution-name v2.0.0 template-bucket-name"
echo "For example: ./build-s3-dist.sh solutions trademarked-solution-name v2.0.1 template-bucket-name"
exit 1
fi

Expand Down
2 changes: 1 addition & 1 deletion source/infrastructure/cdk.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"@custom-bundler/unit-test": false,
"solution_id": "SO0276",
"solution_name": "generative-ai-application-builder-on-aws",
"solution_version": "v2.0.0",
"solution_version": "v2.0.1",
"app_registry_name": "GAAB",
"application_type": "AWS-Solutions",
"application_trademark_name": "Generative AI Application Builder on AWS",
Expand Down
114 changes: 111 additions & 3 deletions source/infrastructure/lib/api/deployment-platform-rest-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import * as lambda from 'aws-cdk-lib/aws-lambda';

import { ApiGatewayToLambda } from '@aws-solutions-constructs/aws-apigateway-lambda';
import { WafwebaclToApiGateway } from '@aws-solutions-constructs/aws-wafwebacl-apigateway';
import { DefaultWafRules } from '@aws-solutions-constructs/core';
import { wrapManagedRuleSet } from '@aws-solutions-constructs/core';
import { CfnWebACL } from 'aws-cdk-lib/aws-wafv2';
import { NagSuppressions } from 'cdk-nag';
import { Construct } from 'constructs';
Expand Down Expand Up @@ -70,9 +70,15 @@ export class DeploymentPlatformRestEndpoint extends Construct {
*/
private readonly stack: cdk.Stack;

/**
* Counter for the priority of custom WAF rules
*/
private rulePriorityCounter: number;

constructor(scope: Construct, id: string, props: DeploymentPlatformRestEndpointProps) {
super(scope, id);
this.stack = cdk.Stack.of(scope);
this.rulePriorityCounter = CUSTOM_RULE_PRIORITY;

const lambdaRestApi = new ApiGatewayToLambda(this, 'EndPoint', {
existingLambdaObj: props.useCaseManagementAPILambda,
Expand Down Expand Up @@ -108,7 +114,17 @@ export class DeploymentPlatformRestEndpoint extends Construct {
webaclProps: {
defaultAction: { allow: {} },
scope: 'REGIONAL',
rules: [...DefaultWafRules(), this.defineBlockRequestHeadersRule()],
rules: [
wrapManagedRuleSet('AWSManagedRulesBotControlRuleSet', 'AWS', 0),
wrapManagedRuleSet('AWSManagedRulesKnownBadInputsRuleSet', 'AWS', 1),
this.defineAWSManagedRulesCommonRuleSetWithBodyOverride(2),
wrapManagedRuleSet('AWSManagedRulesAnonymousIpList', 'AWS', 3),
wrapManagedRuleSet('AWSManagedRulesAmazonIpReputationList', 'AWS', 4),
wrapManagedRuleSet('AWSManagedRulesAdminProtectionRuleSet', 'AWS', 5),
wrapManagedRuleSet('AWSManagedRulesSQLiRuleSet', 'AWS', 6),
this.defineBlockRequestHeadersRule(),
this.defineBlockOversizedBodyNotInDeployRule()
],
customResponseBodies: {
[HEADERS_NOT_ALLOWED_KEY]: this.createHeadersNotAllowedResponse()
}
Expand Down Expand Up @@ -474,7 +490,7 @@ export class DeploymentPlatformRestEndpoint extends Construct {
*/
private defineBlockRequestHeadersRule(): CfnWebACL.RuleProperty {
return {
priority: CUSTOM_RULE_PRIORITY,
priority: this.getCustomRulePriority(),
name: 'Custom-BlockRequestHeaders',
action: {
block: {
Expand Down Expand Up @@ -515,4 +531,96 @@ export class DeploymentPlatformRestEndpoint extends Construct {
contentType: 'TEXT_PLAIN'
};
}

/**
* Define WAF rule which enforces the SizeRestrictions_Body rule from the core rule set for URIs not in the /deployments path
* @returns WAF rule
*/
private defineBlockOversizedBodyNotInDeployRule(): CfnWebACL.RuleProperty {
return {
priority: this.getCustomRulePriority(),
name: 'Custom-BlockOversizedBodyNotInDeploy',
action: {
block: {}
},
visibilityConfig: {
cloudWatchMetricsEnabled: true,
metricName: 'Custom-BlockOversizedBodyNotInDeploy',
sampledRequestsEnabled: true
},
statement: {
andStatement: {
statements: [
{
labelMatchStatement: {
scope: 'LABEL',
key: 'awswaf:managed:aws:core-rule-set:SizeRestrictions_Body'
}
},
{
notStatement: {
statement: {
byteMatchStatement: {
searchString: '/deployments',
fieldToMatch: {
uriPath: {}
},
textTransformations: [
{
priority: 0,
type: 'NONE'
}
],
positionalConstraint: 'ENDS_WITH'
}
}
}
}
]
}
}
};
}

/**
* Defines a WAF rule which enforces the AWSManagedRulesCommonRuleSet, with an override to only count the SizeRestrictions_BODY.
* @param priority The priority of the rule
* @returns The WAF rule
*/
private defineAWSManagedRulesCommonRuleSetWithBodyOverride(priority: number): CfnWebACL.RuleProperty {
return {
name: 'AWS-AWSManagedRulesCommonRuleSet',
priority: priority,
statement: {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesCommonRuleSet',
ruleActionOverrides: [
{
name: 'SizeRestrictions_BODY',
actionToUse: {
count: {}
}
}
]
}
},
overrideAction: {
none: {}
},
visibilityConfig: {
sampledRequestsEnabled: true,
cloudWatchMetricsEnabled: true,
metricName: 'AWS-AWSManagedRulesCommonRuleSet'
}
};
}

/**
* Gets a unique priority for a custom rule, incrementing an internal counter
* @returns A unique priority for each custom rule
*/
private getCustomRulePriority(): number {
return this.rulePriorityCounter++;
}
}
33 changes: 14 additions & 19 deletions source/infrastructure/lib/api/websocket-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@
* 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. *
**********************************************************************************************************************/
*********************************************************************************************************************/

import { ApiGatewayV2WebSocketToSqs } from '@aws-solutions-constructs/aws-apigatewayv2websocket-sqs';
import { SqsToLambda } from '@aws-solutions-constructs/aws-sqs-lambda';
import * as cdk from 'aws-cdk-lib';
import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2';
import { WebSocketApi, WebSocketStage } from 'aws-cdk-lib/aws-apigatewayv2';
import { WebSocketLambdaAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
import { WebSocketAwsIntegration, WebSocketLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
import { WebSocketLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import { NagSuppressions } from 'cdk-nag';
import { Construct } from 'constructs';
import { ApiGatewayV2WebSocketToSqs } from '../framework/aws-apigwv2websocket-sqs';
import { LOG_RETENTION_PERIOD } from '../utils/constants';

export interface WebSocketProps {
Expand Down Expand Up @@ -99,21 +98,10 @@ export class WebSocketEndpoint extends Construct {
maxReceiveCount: 3,
logGroupProps: {
retention: LOG_RETENTION_PERIOD
}
});

apiGatewayV2WebSocketToSqs.webSocketApi.addRoute('sendMessage', {
integration: new WebSocketAwsIntegration('DefaultIntegration', {
integrationMethod: apigwv2.HttpMethod.POST,
integrationUri: `arn:${cdk.Aws.PARTITION}:apigateway:${cdk.Aws.REGION}:sqs:path/${cdk.Aws.ACCOUNT_ID}/${apiGatewayV2WebSocketToSqs.sqsQueue.queueName}`,
requestTemplates: { 'sendMessage': requestTemplate },
templateSelectionExpression: 'sendMessage',
passthroughBehavior: apigwv2.PassthroughBehavior.NEVER,
credentialsRole: apiGatewayV2WebSocketToSqs.apiGatewayRole,
requestParameters: {
'integration.request.header.Content-Type': "'application/x-www-form-urlencoded'"
}
})
},
createDefaultRoute: false,
customRouteName: 'sendMessage',
defaultRouteRequestTemplate: { 'sendMessage': requestTemplate }
});

// the socket URL to post responses
Expand Down Expand Up @@ -159,5 +147,12 @@ export class WebSocketEndpoint extends Construct {
}
]
);

NagSuppressions.addResourceSuppressions(this.websocketApiStage, [
{
id: 'AwsSolutions-APIG1',
reason: 'Access logging configuration has been provided as per ApiGateway v2 requirements'
}
]);
}
}
1 change: 1 addition & 0 deletions source/infrastructure/lib/bedrock-chat-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export class BedrockChat extends UseCaseChat {
customResourceLambdaArn: this.applicationSetup.customResourceLambda.functionArn,
customResourceRoleArn: this.applicationSetup.customResourceLambda.role!.roleArn,
iPamPoolId: this.iPamPoolId.valueAsString,
accessLogBucket: this.applicationSetup.accessLoggingBucket,
...this.baseStackProps
});
}
Expand Down
6 changes: 5 additions & 1 deletion source/infrastructure/lib/deployment-platform-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export class DeploymentPlatformStack extends BaseStack {
cloudFrontUrl: uiInfrastructureBuilder.getCloudFrontUrlWithCondition(),
deployWebApp: this.deployWebApp.valueAsString,
deployWebAppCondition: uiInfrastructureBuilder.deployWebAppCondition,
accessLoggingBucket: this.applicationSetup.accessLoggingBucket,
...this.baseStackProps
});

Expand All @@ -160,6 +161,7 @@ export class DeploymentPlatformStack extends BaseStack {
modelInfoApiLambda: this.useCaseManagementSetup.useCaseManagement.modelInfoApiLambda,
customResourceLambda: this.applicationSetup.customResourceLambda,
customResourceRole: this.applicationSetup.customResourceRole,
accessLoggingBucket: this.applicationSetup.accessLoggingBucket,
...this.baseStackProps
});

Expand Down Expand Up @@ -196,7 +198,8 @@ export class DeploymentPlatformStack extends BaseStack {
CustomResourceRoleArn: this.applicationSetup.customResourceLambda.role!.roleArn,
CustomResourceLambdaArn: this.applicationSetup.customResourceLambda.functionArn,
WebConfigKey: webConfigSsmKey,
WebS3BucketArn: this.uiDistribution.websiteBucket.bucketArn
WebS3BucketArn: this.uiDistribution.websiteBucket.bucketArn,
AccessLoggingBucketArn: this.applicationSetup.accessLoggingBucket.bucketArn
},
description: `Custom resource that copies UI assets to S3 bucket - Version ${props.solutionVersion}`
});
Expand Down Expand Up @@ -274,6 +277,7 @@ export class DeploymentPlatformStack extends BaseStack {
customResourceLambdaArn: this.applicationSetup.customResourceLambda.functionArn,
customResourceRoleArn: this.applicationSetup.customResourceLambda.role!.roleArn,
iPamPoolId: this.iPamPoolId.valueAsString,
accessLogBucket: this.applicationSetup.accessLoggingBucket,
...this.baseStackProps
});
}
Expand Down
82 changes: 82 additions & 0 deletions source/infrastructure/lib/framework/base-nested-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env node
/**********************************************************************************************************************
* 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. *
*********************************************************************************************************************/

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

/**
* Stack properties for nested stack
*/
export abstract class BaseNestedStack extends cdk.NestedStack {
/**
* The custom resource lambda arn
*/
public customResourceLambdaArn: string;

/**
* The custom resource lambda role arn
*/
public customResourceLambdaRoleArn: string;

/**
* Access logging bucket to be associated with any S3 bucket creation
*/
public readonly accessLoggingBucket: string;

constructor(scope: Construct, id: string, props?: cdk.NestedStackProps) {
super(scope, id, props);
const stack = cdk.Stack.of(this);
this.customResourceLambdaArn = new cdk.CfnParameter(stack, 'CustomResourceLambdaArn', {
type: 'String',
description: 'The custom resource lambda arn',
allowedPattern:
'^arn:(aws[a-zA-Z-]*)?:lambda:[a-z]{2}(-gov)?-[a-z]+-\\d{1}:\\d{12}:function:[a-zA-Z0-9-_]+(:(\\$LATEST|[a-zA-Z0-9-_]+))?$',
constraintDescription: 'Please provide a valid lambda arn.'
}).valueAsString;

this.customResourceLambdaRoleArn = new cdk.CfnParameter(stack, 'CustomResourceRoleArn', {
type: 'String',
description: 'The custom resource lambda role arn',
allowedPattern: '^arn:(aws|aws-cn|aws-us-gov):iam::\\d{12}:role/[a-zA-Z_0-9+=,.@\\-_/]+$',
constraintDescription: 'Please provide a valid lambda role arn.'
}).valueAsString;

this.accessLoggingBucket = new cdk.CfnParameter(stack, 'AccessLoggingBucketArn', {
type: 'String',
allowedPattern: '^arn:(aws|aws-cn|aws-us-gov):s3:::\\S+$',
description: 'Arn of the S3 bucket to use for access logging.'
}).valueAsString;
}
}

export abstract class BaseUseCaseNestedStack extends BaseNestedStack {
/**
* Unique ID for this deployed use case within an application. Provided by the deployment platform if in use.
*/
public readonly useCaseUUID: string;

constructor(scope: Construct, id: string, props?: cdk.NestedStackProps) {
super(scope, id, props);
const stack = cdk.Stack.of(this);

this.useCaseUUID = new cdk.CfnParameter(stack, 'UseCaseUUID', {
type: 'String',
description:
'UUID to identify this deployed use case within an application. Please provide an 8 character long UUID. If you are editing the stack, do not modify the value (retain the value used during creating the stack). A different UUID when editing the stack will result in new AWS resource created and deleting the old ones',
allowedPattern: '^[0-9a-fA-F]{8}$',
maxLength: 8,
constraintDescription: 'Please provide an 8 character long UUID'
}).valueAsString;
}
}
Loading