packages/constructs/L3/ai/gaia-l3-construct/lib/chatbot-api/websocket-api.ts (567 lines of code) (raw):
import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2';
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
import { WebSocketLambdaAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
import { WebSocketLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
import * as cdk from 'aws-cdk-lib';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambdaEventSources from 'aws-cdk-lib/aws-lambda-event-sources';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';
import * as path from 'path';
import { Shared } from '../shared';
import { BackendApisProps, Direction, SystemConfig } from '../shared/types';
import * as cognito from 'aws-cdk-lib/aws-cognito';
import { MdaaL3Construct, MdaaL3ConstructProps } from '@aws-mdaa/l3-construct';
import { MdaaSnsTopic } from '@aws-mdaa/sns-constructs';
import { MdaaLambdaFunction, MdaaLambdaRole } from '@aws-mdaa/lambda-constructs';
import { MdaaRole } from '@aws-mdaa/iam-constructs';
import { MdaaSqsDeadLetterQueue, MdaaSqsQueue } from '@aws-mdaa/sqs-constructs';
import { MdaaDDBTable } from '@aws-mdaa/ddb-constructs';
import { MdaaNagSuppressions } from '@aws-mdaa/construct'; //NOSONAR
import { MdaaKmsKey } from '@aws-mdaa/kms-constructs';
import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { MdaaLogGroup, MdaaLogGroupProps } from '@aws-mdaa/cloudwatch-constructs';
import * as route53 from 'aws-cdk-lib/aws-route53';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import { ApiGatewayv2DomainProperties } from 'aws-cdk-lib/aws-route53-targets';
import * as ssm from 'aws-cdk-lib/aws-ssm';
import { Duration } from 'aws-cdk-lib';
interface WebSocketApiProps extends MdaaL3ConstructProps {
readonly config: SystemConfig;
readonly shared: Shared;
readonly userPool: cognito.IUserPool;
encryptionKey: MdaaKmsKey;
}
export class WebSocketApi extends MdaaL3Construct {
public readonly api: apigwv2.WebSocketApi;
public readonly messagesTopic: sns.Topic;
private readonly props: WebSocketApiProps;
constructor(scope: Construct, id: string, props: WebSocketApiProps) {
super(scope, id, props);
this.props = props;
// Create the main Message Topic acting as a message bus
const messagesTopic = new MdaaSnsTopic(this, 'WeSocketMessagesTopic', {
masterKey: props.encryptionKey,
naming: props.naming,
createParams: true,
createOutputs: false,
topicName: 'WeSocketMessagesTopic',
});
const connectionsTable = new MdaaDDBTable(this, 'ConnectionsTable', {
encryptionKey: props.encryptionKey,
naming: props.naming,
tableName: props.naming.resourceName('Connections'),
createParams: true,
createOutputs: false,
partitionKey: {
name: 'connectionId',
type: dynamodb.AttributeType.STRING,
},
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
connectionsTable.addGlobalSecondaryIndex({
indexName: 'byUser',
partitionKey: { name: 'userId', type: dynamodb.AttributeType.STRING },
});
const vpcNetworkInterfacePolicy = new PolicyStatement({
effect: Effect.ALLOW,
actions: ['ec2:CreateNetworkInterface', 'ec2:DescribeNetworkInterfaces', 'ec2:DeleteNetworkInterface'],
resources: ['*'],
});
const connectionHandlerFunctionRole = new MdaaLambdaRole(this, 'WebSocketConnectionHandlerFunctionRole', {
naming: this.props.naming,
roleName: 'WebSocketConnectionHandlerFunctionRole',
logGroupNames: [this.props.naming.resourceName('websocket-connection-handler')],
createParams: false,
createOutputs: false,
});
connectionHandlerFunctionRole.addToPolicy(vpcNetworkInterfacePolicy);
const connectionHandlerFunction = this.createConnectionHandlerFunction(
connectionHandlerFunctionRole,
props.shared.appSecurityGroup,
connectionsTable,
);
props.encryptionKey.grantEncryptDecrypt(connectionHandlerFunction);
connectionsTable.grantReadWriteData(connectionHandlerFunctionRole);
const authorizerFunctionRole = new MdaaLambdaRole(this, 'WebSocketAuthorizerFunctionRole', {
naming: this.props.naming,
roleName: 'WebSocketAuthorizerFunctionRole',
logGroupNames: [this.props.naming.resourceName('websocket-authorizer')],
createParams: false,
createOutputs: false,
});
authorizerFunctionRole.addToPolicy(vpcNetworkInterfacePolicy);
const authorizerFunction = this.createAuthorizerFunction(props.shared.appSecurityGroup, authorizerFunctionRole);
const webSocketApi = new apigwv2.WebSocketApi(this, 'WebSocketApi', {
connectRouteOptions: {
authorizer: new WebSocketLambdaAuthorizer('Authorizer', authorizerFunction, {
identitySource: ['route.request.querystring.token'],
}),
integration: new WebSocketLambdaIntegration('ConnectIntegration', connectionHandlerFunction),
},
disconnectRouteOptions: {
integration: new WebSocketLambdaIntegration('DisconnectIntegration', connectionHandlerFunction),
},
});
if (this.props.config?.api?.socketApiDomainName === undefined) {
new ssm.StringParameter(this, 'SocketApiIdSSMParam', {
parameterName: this.props.naming.ssmPath('socket/api/id'),
stringValue: webSocketApi.apiId,
});
}
const stage = new apigwv2.WebSocketStage(this, 'WebSocketApiStage', {
webSocketApi,
stageName: 'socket',
autoDeploy: true,
});
const apiCustomDomainConfigs = props.config.api;
if (apiCustomDomainConfigs !== undefined) {
this.applyCustomDomain(apiCustomDomainConfigs, webSocketApi, stage);
}
const apiAccessLogGroupProps: MdaaLogGroupProps = {
logGroupName: 'genai-admin-websocket-api',
encryptionKey: this.props.encryptionKey,
// WAF log group destination names must start with aws-waf-logs-
// https://docs.aws.amazon.com/waf/latest/developerguide/logging-cw-logs.html
logGroupNamePathPrefix: 'genai-admin-websocket-api-access-logs-',
retention: RetentionDays.INFINITE,
naming: this.props.naming,
createParams: false,
createOutputs: false,
};
const apiAccessLogGroup = new MdaaLogGroup(this, 'WebSocketApiLogGroup', apiAccessLogGroupProps);
const cfnStage = stage.node.defaultChild as unknown as apigwv2.CfnStage;
cfnStage.accessLogSettings = {
destinationArn: apiAccessLogGroup.logGroupArn,
format: JSON.stringify({
requestId: '$context.requestId',
ip: '$context.identity.sourceIp',
caller: '$context.identity.caller',
user: '$context.identity.user',
connectionId: '$context.connectionId',
}),
};
const incomingMessageHandlerFunctionRole = new MdaaLambdaRole(this, 'WebSocketIncomingMessageHandlerFunctionRole', {
naming: props.naming,
roleName: 'WebSocketIncomingMessageHandlerFunctionRole',
logGroupNames: [props.naming.resourceName('websocket-incoming-message-handler')],
createParams: false,
createOutputs: false,
});
incomingMessageHandlerFunctionRole.addToPolicy(vpcNetworkInterfacePolicy);
const incomingMessageHandlerFunction = this.createIncomingMessageHandlerFunction(
incomingMessageHandlerFunctionRole,
props.shared.appSecurityGroup,
messagesTopic,
stage,
);
props.encryptionKey.grantEncryptDecrypt(incomingMessageHandlerFunctionRole);
messagesTopic.grantPublish(incomingMessageHandlerFunctionRole);
incomingMessageHandlerFunctionRole.addToPolicy(
new iam.PolicyStatement({
actions: ['events:PutEvents'],
resources: [`arn:${cdk.Aws.PARTITION}:events:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:event-bus/default`],
}),
);
incomingMessageHandlerFunctionRole.addToPolicy(vpcNetworkInterfacePolicy);
webSocketApi.addRoute('$default', {
integration: new WebSocketLambdaIntegration('DefaultIntegration', incomingMessageHandlerFunction),
});
const outgoingMessageHandlerFunctionRole = new MdaaLambdaRole(this, 'WebSocketOutgoingMessageFunctionRole', {
naming: props.naming,
roleName: 'WebSocketOutgoingMessageHandlerFunctionRole',
logGroupNames: [props.naming.resourceName('websocket-outgoing-message-handler')],
createParams: false,
createOutputs: false,
});
props.encryptionKey.grantEncryptDecrypt(outgoingMessageHandlerFunctionRole);
outgoingMessageHandlerFunctionRole.addToPolicy(vpcNetworkInterfacePolicy);
const outgoingMessageHandlerFunction = this.createOutgoingMessageHandlerFunction(
outgoingMessageHandlerFunctionRole,
connectionsTable,
stage,
);
connectionsTable.grantReadData(outgoingMessageHandlerFunctionRole);
outgoingMessageHandlerFunctionRole.addToPolicy(
new iam.PolicyStatement({
actions: ['execute-api:ManageConnections'],
resources: [
`arn:${cdk.Aws.PARTITION}:execute-api:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:${webSocketApi.apiId}/${stage.stageName}/*/*`,
],
}),
);
const deadLetterQueue = new MdaaSqsDeadLetterQueue(this, 'WebSocketOutgoingMessagesDLQ', {
encryptionMasterKey: props.encryptionKey,
naming: props.naming,
queueName: 'WebSocketOutgoingMessagesDLQ',
createParams: false,
createOutputs: false,
});
const queue = new MdaaSqsQueue(this, 'WebSocketOutgoingMessagesQueue', {
encryptionMasterKey: props.encryptionKey,
naming: props.naming,
createParams: false,
createOutputs: false,
queueName: 'WebSocketOutgoingMessagesQueue',
deadLetterQueue: {
queue: deadLetterQueue,
maxReceiveCount: 3,
},
});
// grant eventbridge permissions to send messages to the queue
queue.addToResourcePolicy(
new iam.PolicyStatement({
actions: ['sqs:SendMessage'],
resources: [queue.queueArn],
principals: [new iam.ServicePrincipal('events.amazonaws.com'), new iam.ServicePrincipal('sqs.amazonaws.com')],
}),
);
outgoingMessageHandlerFunction.addEventSource(new lambdaEventSources.SqsEventSource(queue));
// Route all outgoing messages to the websocket interface queue
messagesTopic.addSubscription(
new subscriptions.SqsSubscription(queue, {
filterPolicyWithMessageBody: {
direction: sns.FilterOrPolicy.filter(
sns.SubscriptionFilter.stringFilter({
allowlist: [Direction.OUT],
}),
),
},
}),
);
MdaaNagSuppressions.addCodeResourceSuppressions(
incomingMessageHandlerFunctionRole,
[
{
id: 'AwsSolutions-IAM5',
reason:
'X-Ray actions only support wildcard and execute api manage connections restricted to stack api gateway',
},
{ id: 'NIST.800.53.R5-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
{ id: 'HIPAA.Security-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
{ id: 'PCI.DSS.321-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
],
true,
);
MdaaNagSuppressions.addCodeResourceSuppressions(
outgoingMessageHandlerFunctionRole,
[
{
id: 'AwsSolutions-IAM5',
reason:
'X-Ray actions only support wildcard and execute api manage connections restricted to stack api gateway, and AWSLambdaBasicExecutionRole restrictive enough',
},
{ id: 'NIST.800.53.R5-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
{ id: 'HIPAA.Security-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
{ id: 'PCI.DSS.321-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
],
true,
);
MdaaNagSuppressions.addCodeResourceSuppressions(
webSocketApi,
[{ id: 'AwsSolutions-APIG4', reason: 'API guarded with Cognito Auth and an Authorizer lambda' }],
true,
);
this.api = webSocketApi;
this.messagesTopic = messagesTopic;
}
private createOutgoingMessageHandlerFunction(
outgoingMessageHandlerFunctionRole: MdaaRole,
connectionsTable: dynamodb.ITable,
stage: apigwv2.WebSocketStage,
) {
const outgoingMessageHandlerCodePath =
this.props.config?.codeOverwrites?.webSocketOutgoingMessageHandlerCodePath !== undefined
? this.props.config.codeOverwrites.webSocketOutgoingMessageHandlerCodePath
: path.join(__dirname, './functions/outgoing-message-handler');
const outgoingMessageHandlerFunction = new MdaaLambdaFunction(this, 'WebSocketOutgoingMessageFunction', {
functionName: 'websocket-outgoing-message-handler',
naming: this.props.naming,
createParams: false,
createOutputs: false,
role: outgoingMessageHandlerFunctionRole,
code: lambda.Code.fromAsset(outgoingMessageHandlerCodePath),
handler: 'index.handler',
runtime: this.props.shared.pythonRuntime,
architecture: this.props.shared.lambdaArchitecture,
timeout: Duration.seconds(6),
tracing: lambda.Tracing.ACTIVE,
layers: [this.props.shared.powerToolsLayer],
environment: {
...this.props.shared.defaultEnvironmentVariables,
WEBSOCKET_API_ENDPOINT: stage.callbackUrl,
CONNECTIONS_TABLE_NAME: connectionsTable.tableName,
},
reservedConcurrentExecutions: this.props.config?.concurrency?.websocketConcurrentLambdas || 1,
});
MdaaNagSuppressions.addCodeResourceSuppressions(
outgoingMessageHandlerFunction,
[
{
id: 'NIST.800.53.R5-LambdaInsideVPC',
reason: 'Lambda in vpc is not enforced, outgoing messages just communicates to socket gateway.',
},
{
id: 'HIPAA.Security-LambdaInsideVPC',
reason: 'Lambda in vpc is not enforced, outgoing messages just communicates to socket gateway.',
},
{
id: 'PCI.DSS.321-LambdaInsideVPC',
reason: 'Lambda in vpc is not enforced, outgoing messages just communicates to socket gateway.',
},
{ id: 'NIST.800.53.R5-LambdaDLQ', reason: 'Function is API implementation and will be invoked synchronously.' },
{
id: 'NIST.800.53.R5-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'HIPAA.Security-LambdaDLQ',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'HIPAA.Security-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'PCI.DSS.321-LambdaDLQ',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'PCI.DSS.321-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
],
true,
);
return outgoingMessageHandlerFunction;
}
private createIncomingMessageHandlerFunction(
incomingMessageHandlerFunctionRole: MdaaRole,
apiSecurityGroup: ec2.ISecurityGroup,
messagesTopic: sns.ITopic,
stage: apigwv2.WebSocketStage,
) {
const incomingMessageHandlerCodePath =
this.props.config?.codeOverwrites?.webSocketIncomingMessageHandlerCodePath !== undefined
? this.props.config.codeOverwrites.webSocketIncomingMessageHandlerCodePath
: path.join(__dirname, './functions/incoming-message-handler');
const incomingMessageHandlerFunction = new MdaaLambdaFunction(this, 'WebSocketIncomingMessageHandlerFunction', {
functionName: 'websocket-incoming-message-handler',
naming: this.props.naming,
createParams: false,
createOutputs: false,
role: incomingMessageHandlerFunctionRole,
code: lambda.Code.fromAsset(incomingMessageHandlerCodePath),
vpc: this.props.shared.vpc,
securityGroups: [apiSecurityGroup],
vpcSubnets: { subnets: this.props.shared.appSubnets },
handler: 'index.handler',
runtime: this.props.shared.pythonRuntime,
architecture: this.props.shared.lambdaArchitecture,
timeout: Duration.seconds(6),
tracing: lambda.Tracing.ACTIVE,
layers: [this.props.shared.powerToolsLayer],
environment: {
...this.props.shared.defaultEnvironmentVariables,
MESSAGES_TOPIC_ARN: messagesTopic.topicArn,
WEBSOCKET_API_ENDPOINT: stage.callbackUrl,
},
reservedConcurrentExecutions: this.props.config?.concurrency?.websocketConcurrentLambdas || 1,
});
MdaaNagSuppressions.addCodeResourceSuppressions(
incomingMessageHandlerFunction,
[
{ id: 'NIST.800.53.R5-LambdaDLQ', reason: 'Function is API implementation and will be invoked synchronously.' },
{
id: 'NIST.800.53.R5-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'HIPAA.Security-LambdaDLQ',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'HIPAA.Security-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'PCI.DSS.321-LambdaDLQ',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'PCI.DSS.321-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
],
true,
);
return incomingMessageHandlerFunction;
}
private createAuthorizerFunction(apiSecurityGroup: ec2.ISecurityGroup, authorizerFunctionRole: iam.IRole) {
const authorizerFunctionCodePath =
this.props.config?.codeOverwrites?.webSocketAuthorizerFunctionCodePath !== undefined
? this.props.config.codeOverwrites.webSocketAuthorizerFunctionCodePath
: path.join(__dirname, './functions/authorizer');
const authorizerFunction = new MdaaLambdaFunction(this, 'WebSocketAuthorizerFunction', {
functionName: 'websocket-authorizer',
naming: this.props.naming,
createParams: false,
createOutputs: false,
role: authorizerFunctionRole,
code: lambda.Code.fromAsset(authorizerFunctionCodePath),
vpc: this.props.shared.vpc,
securityGroups: [apiSecurityGroup],
vpcSubnets: { subnets: this.props.shared.appSubnets },
handler: 'index.handler',
runtime: this.props.shared.pythonRuntime,
architecture: this.props.shared.lambdaArchitecture,
timeout: Duration.seconds(6),
tracing: lambda.Tracing.ACTIVE,
layers: [this.props.shared.powerToolsLayer],
environment: {
...this.props.shared.defaultEnvironmentVariables,
},
reservedConcurrentExecutions: this.props.config?.concurrency?.websocketConcurrentLambdas || 1,
});
MdaaNagSuppressions.addCodeResourceSuppressions(
authorizerFunction,
[
{ id: 'NIST.800.53.R5-LambdaDLQ', reason: 'Function is API implementation and will be invoked synchronously.' },
{
id: 'NIST.800.53.R5-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'HIPAA.Security-LambdaDLQ',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'HIPAA.Security-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'PCI.DSS.321-LambdaDLQ',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'PCI.DSS.321-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
],
true,
);
this.props.userPool.grant(authorizerFunctionRole, 'cognito-idp:GetUser');
MdaaNagSuppressions.addCodeResourceSuppressions(
authorizerFunctionRole,
[
{ id: 'AwsSolutions-IAM5', reason: 'X-Ray actions only support wildcard.' },
{ id: 'NIST.800.53.R5-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
{ id: 'HIPAA.Security-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
{ id: 'PCI.DSS.321-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
],
true,
);
return authorizerFunction;
}
private applyCustomDomain(
apiCustomDomainProps: BackendApisProps,
api: apigwv2.WebSocketApi,
stage: apigwv2.WebSocketStage,
) {
const hostedZone = route53.HostedZone.fromLookup(this, 'WebSocketApiMainHostedZone', {
domainName: apiCustomDomainProps.hostedZoneName,
});
const domainName = new apigwv2.DomainName(this, 'WebSocketApiDomainName', {
domainName: apiCustomDomainProps.socketApiDomainName,
certificate: new acm.Certificate(this, 'Certificate', {
domainName: apiCustomDomainProps.socketApiDomainName,
validation: acm.CertificateValidation.fromDns(hostedZone),
}),
});
new apigwv2.CfnApiMapping(this, 'Mapping', {
apiId: api.apiId,
domainName: domainName.name,
stage: stage.stageName,
apiMappingKey: 'socket',
});
new route53.ARecord(this, 'DnsRecord', {
recordName: apiCustomDomainProps.socketApiDomainName,
zone: hostedZone,
target: route53.RecordTarget.fromAlias(
new ApiGatewayv2DomainProperties(domainName.regionalDomainName, domainName.regionalHostedZoneId),
),
});
}
private createConnectionHandlerFunction(
connectionHandlerFunctionRole: iam.IRole,
apiSecurityGroup: ec2.ISecurityGroup,
connectionsTable: dynamodb.ITable,
) {
const connectionHandlerCodePath =
this.props.config?.codeOverwrites?.webSocketConnectionHandlerCodePath !== undefined
? this.props.config.codeOverwrites.webSocketConnectionHandlerCodePath
: path.join(__dirname, './functions/connection-handler');
const connectionHandlerFunction = new MdaaLambdaFunction(this, 'WebSocketConnectionHandlerFunction', {
functionName: 'websocket-connection-handler',
naming: this.props.naming,
createParams: false,
createOutputs: false,
role: connectionHandlerFunctionRole,
code: lambda.Code.fromAsset(connectionHandlerCodePath),
vpc: this.props.shared.vpc,
securityGroups: [apiSecurityGroup],
vpcSubnets: { subnets: this.props.shared.appSubnets },
handler: 'index.handler',
runtime: this.props.shared.pythonRuntime,
architecture: this.props.shared.lambdaArchitecture,
timeout: Duration.seconds(6),
tracing: lambda.Tracing.ACTIVE,
layers: [this.props.shared.powerToolsLayer],
environment: {
...this.props.shared.defaultEnvironmentVariables,
CONNECTIONS_TABLE_NAME: connectionsTable.tableName,
},
reservedConcurrentExecutions: this.props.config?.concurrency?.websocketConcurrentLambdas || 1,
});
MdaaNagSuppressions.addCodeResourceSuppressions(
connectionHandlerFunction,
[
{ id: 'NIST.800.53.R5-LambdaDLQ', reason: 'Function is API implementation and will be invoked synchronously.' },
{
id: 'NIST.800.53.R5-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'HIPAA.Security-LambdaDLQ',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'HIPAA.Security-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'PCI.DSS.321-LambdaDLQ',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
{
id: 'PCI.DSS.321-LambdaConcurrency',
reason: 'Function is API implementation and will be invoked via API Gateway with WAF protections.',
},
],
true,
);
MdaaNagSuppressions.addCodeResourceSuppressions(
connectionHandlerFunctionRole,
[
{
id: 'AwsSolutions-IAM5',
reason: 'X-Ray actions only support wildcard and log group not known at deployment.',
},
{ id: 'NIST.800.53.R5-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
{ id: 'HIPAA.Security-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
{ id: 'PCI.DSS.321-IAMNoInlinePolicy', reason: 'Inline policy managed by MDAA framework.' },
],
true,
);
return connectionHandlerFunction;
}
}