Skip to content

[Rapticore] Security Findings - hamza/RCOR-2704 (Part 1)#39

Draft
rapticore-github[bot] wants to merge 94 commits into
rapticore/tracking-hamza/RCOR-2704-1from
hamza/RCOR-2704
Draft

[Rapticore] Security Findings - hamza/RCOR-2704 (Part 1)#39
rapticore-github[bot] wants to merge 94 commits into
rapticore/tracking-hamza/RCOR-2704-1from
hamza/RCOR-2704

Conversation

@rapticore-github

Copy link
Copy Markdown

Rapticore Security Findings (Part 1)

This is a draft PR containing automated security fix recommendations from Rapticore.

⚠️ This is part 1 of the findings for this branch.

How to use this PR:

  1. Review the suggested fixes in the comments below
  2. Apply fixes manually to your branch or use the provided code snippets
  3. This PR will be automatically updated as new findings are discovered

🤖 Generated by Rapticore

rrrix and others added 30 commits April 21, 2020 12:06
Co-authored-by: Azeem Irshad <azeem.irshad@rapticore.com>
Co-authored-by: Azeem Irshad <azeem.irshad@rapticore.com>
changed the name of the standard cfn to standardsomething
Comment on lines +376 to +575
Action:
- sts:AssumeRole
- sts:TagSession
Resource: "arn:aws:iam::*:role/rapticore-cross-account-*"
# IAM and EC2 Read-Only (needed by discovery for role enumeration)
- Effect: Allow
Action:
- iam:ListRole*
- iam:GetRole*
- ec2:DescribeRegions
Resource: "*"
# Cognito Identity (needed for Azure workload identity)
- Effect: Allow
Action:
- cognito-identity:GetOpenIdTokenForDeveloperIdentity
Resource: !Sub "arn:aws:cognito-identity:${AWS::Region}:${AWS::AccountId}:identitypool/*"

#####
# AWS Load Balancer Controller IAM Role (IRSA)
#####
AwsLoadBalancerControllerRole:
Type: AWS::IAM::Role
DependsOn: OidcProvider
Properties:
RoleName: !Sub "${ClusterName}-aws-load-balancer-controller-role"
AssumeRolePolicyDocument:
Fn::Sub:
- |
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${AWS::AccountId}:oidc-provider/${OidcUrl}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"${OidcUrl}:aud": "sts.amazonaws.com",
"${OidcUrl}:sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
}
}
}]
}
- OidcUrl: !Select [1, !Split ["https://", !GetAtt EksCluster.OpenIdConnectIssuerUrl]]
Policies:
- PolicyName: AWSLoadBalancerControllerPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- iam:CreateServiceLinkedRole
Resource: "*"
Condition:
StringEquals:
iam:AWSServiceName: elasticloadbalancing.amazonaws.com
- Effect: Allow
Action:
- ec2:DescribeAccountAttributes
- ec2:DescribeAddresses
- ec2:DescribeAvailabilityZones
- ec2:DescribeInternetGateways
- ec2:DescribeVpcs
- ec2:DescribeVpcPeeringConnections
- ec2:DescribeSubnets
- ec2:DescribeSecurityGroups
- ec2:DescribeInstances
- ec2:DescribeNetworkInterfaces
- ec2:DescribeTags
- ec2:DescribeRouteTables
- ec2:GetCoipPoolUsage
- ec2:DescribeCoipPools
- elasticloadbalancing:DescribeLoadBalancers
- elasticloadbalancing:DescribeLoadBalancerAttributes
- elasticloadbalancing:DescribeListeners
- elasticloadbalancing:DescribeListenerAttributes
- elasticloadbalancing:DescribeListenerCertificates
- elasticloadbalancing:DescribeSSLPolicies
- elasticloadbalancing:DescribeRules
- elasticloadbalancing:DescribeTargetGroups
- elasticloadbalancing:DescribeTargetGroupAttributes
- elasticloadbalancing:DescribeTargetHealth
- elasticloadbalancing:DescribeTags
Resource: "*"
- Effect: Allow
Action:
- cognito-idp:DescribeUserPoolClient
- acm:ListCertificates
- acm:DescribeCertificate
- iam:ListServerCertificates
- iam:GetServerCertificate
- waf-regional:GetWebACL
- waf-regional:GetWebACLForResource
- waf-regional:AssociateWebACL
- waf-regional:DisassociateWebACL
- wafv2:GetWebACL
- wafv2:GetWebACLForResource
- wafv2:AssociateWebACL
- wafv2:DisassociateWebACL
- shield:GetSubscriptionState
- shield:DescribeProtection
- shield:CreateProtection
- shield:DeleteProtection
Resource: "*"
- Effect: Allow
Action:
- ec2:AuthorizeSecurityGroupIngress
- ec2:RevokeSecurityGroupIngress
Resource: "*"
- Effect: Allow
Action:
- ec2:CreateSecurityGroup
Resource: "*"
- Effect: Allow
Action:
- ec2:CreateTags
Resource: "arn:aws:ec2:*:*:security-group/*"
Condition:
StringEquals:
ec2:CreateAction: CreateSecurityGroup
"Null":
aws:RequestTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- ec2:CreateTags
- ec2:DeleteTags
Resource: "arn:aws:ec2:*:*:security-group/*"
Condition:
"Null":
aws:RequestTag/elbv2.k8s.aws/cluster: "true"
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- ec2:AuthorizeSecurityGroupIngress
- ec2:RevokeSecurityGroupIngress
- ec2:DeleteSecurityGroup
Resource: "*"
Condition:
"Null":
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- elasticloadbalancing:CreateLoadBalancer
- elasticloadbalancing:CreateTargetGroup
Resource: "*"
Condition:
"Null":
aws:RequestTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- elasticloadbalancing:CreateListener
- elasticloadbalancing:DeleteListener
- elasticloadbalancing:CreateRule
- elasticloadbalancing:DeleteRule
Resource: "*"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
- elasticloadbalancing:RemoveTags
Resource:
- "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
- "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*"
- "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
Condition:
"Null":
aws:RequestTag/elbv2.k8s.aws/cluster: "true"
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
- elasticloadbalancing:RemoveTags
Resource:
- "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*"
- "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*"
- "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*"
- "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*"
- Effect: Allow
Action:
- elasticloadbalancing:ModifyLoadBalancerAttributes
- elasticloadbalancing:SetIpAddressType
- elasticloadbalancing:SetSecurityGroups
- elasticloadbalancing:SetSubnets
- elasticloadbalancing:DeleteLoadBalancer
- elasticloadbalancing:ModifyTargetGroup
- elasticloadbalancing:ModifyTargetGroupAttributes
- elasticloadbalancing:DeleteTargetGroup
Resource: "*"
Condition:
"Null":
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
Resource:
- "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
- "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*"
- "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
Condition:
StringEquals:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: IAM Policy Allows Unsafe Write Access

Severity: High
Lines: 396-593

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0019)

Description

The IAM policy grants write access to various resources without sufficient constraints, increasing the risk of unauthorized modifications and data breaches. This policy permits actions like creating, tagging, and modifying resources, which can lead to significant security and compliance violations.

Remediation Summary

Fix Required

• Add resource constraints to overly permissive write actions that use Resource: "*". Statements allowing ec2:AuthorizeSecurityGroupIngress, ec2:RevokeSecurityGroupIngress, ec2:CreateSecurityGroup, elasticloadbalancing:CreateListener, elasticloadbalancing:DeleteListener, elasticloadbalancing:CreateRule, elasticloadbalancing:DeleteRule, and elasticloadbalancing:SetWebAcl/ModifyListener/ModifyRule currently lack resource or condition constraints.
• For statements without conditions, add ARN-based resource constraints (e.g., limit security group operations to specific VPC or tag-based resources).
• For elasticloadbalancing:CreateListener, DeleteListener, CreateRule, DeleteRule, SetWebAcl, ModifyListener, AddListenerCertificates, RemoveListenerCertificates, ModifyRule: add conditions checking for the elbv2.k8s.aws/cluster tag or constrain Resource ARNs to cluster-managed load balancers/listeners only.

Code Pattern

Instead of:

- Effect: Allow
  Action:
    - ec2:AuthorizeSecurityGroupIngress
    - ec2:RevokeSecurityGroupIngress
  Resource: "*"

Use:

- Effect: Allow
  Action:
    - ec2:AuthorizeSecurityGroupIngress
    - ec2:RevokeSecurityGroupIngress
  Resource: "arn:aws:ec2:*:*:security-group/*"
  Condition:
    "Null":
      aws:ResourceTag/elbv2.k8s.aws/cluster: "false"

Verification

Re-scan the policy to confirm all write actions either have specific Resource ARN constraints or conditions limiting scope to tagged/created cluster resources.

Priority

high - Estimated effort: 20 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Action:
- sts:AssumeRole
- sts:TagSession
Resource: "arn:aws:iam::*:role/rapticore-cross-account-*"
# IAM and EC2 Read-Only (needed by discovery for role enumeration)
- Effect: Allow
Action:
- iam:ListRole*
- iam:GetRole*
- ec2:DescribeRegions
Resource: "*"
# Cognito Identity (needed for Azure workload identity)
- Effect: Allow
Action:
- cognito-identity:GetOpenIdTokenForDeveloperIdentity
Resource: !Sub "arn:aws:cognito-identity:${AWS::Region}:${AWS::AccountId}:identitypool/*"
#####
# AWS Load Balancer Controller IAM Role (IRSA)
#####
AwsLoadBalancerControllerRole:
Type: AWS::IAM::Role
DependsOn: OidcProvider
Properties:
RoleName: !Sub "${ClusterName}-aws-load-balancer-controller-role"
AssumeRolePolicyDocument:
Fn::Sub:
- |
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${AWS::AccountId}:oidc-provider/${OidcUrl}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"${OidcUrl}:aud": "sts.amazonaws.com",
"${OidcUrl}:sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
}
}
}]
}
- OidcUrl: !Select [1, !Split ["https://", !GetAtt EksCluster.OpenIdConnectIssuerUrl]]
Policies:
- PolicyName: AWSLoadBalancerControllerPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- iam:CreateServiceLinkedRole
Resource: "*"
Condition:
StringEquals:
iam:AWSServiceName: elasticloadbalancing.amazonaws.com
- Effect: Allow
Action:
- ec2:DescribeAccountAttributes
- ec2:DescribeAddresses
- ec2:DescribeAvailabilityZones
- ec2:DescribeInternetGateways
- ec2:DescribeVpcs
- ec2:DescribeVpcPeeringConnections
- ec2:DescribeSubnets
- ec2:DescribeSecurityGroups
- ec2:DescribeInstances
- ec2:DescribeNetworkInterfaces
- ec2:DescribeTags
- ec2:DescribeRouteTables
- ec2:GetCoipPoolUsage
- ec2:DescribeCoipPools
- elasticloadbalancing:DescribeLoadBalancers
- elasticloadbalancing:DescribeLoadBalancerAttributes
- elasticloadbalancing:DescribeListeners
- elasticloadbalancing:DescribeListenerAttributes
- elasticloadbalancing:DescribeListenerCertificates
- elasticloadbalancing:DescribeSSLPolicies
- elasticloadbalancing:DescribeRules
- elasticloadbalancing:DescribeTargetGroups
- elasticloadbalancing:DescribeTargetGroupAttributes
- elasticloadbalancing:DescribeTargetHealth
- elasticloadbalancing:DescribeTags
Resource: "*"
- Effect: Allow
Action:
- cognito-idp:DescribeUserPoolClient
- acm:ListCertificates
- acm:DescribeCertificate
- iam:ListServerCertificates
- iam:GetServerCertificate
- waf-regional:GetWebACL
- waf-regional:GetWebACLForResource
- waf-regional:AssociateWebACL
- waf-regional:DisassociateWebACL
- wafv2:GetWebACL
- wafv2:GetWebACLForResource
- wafv2:AssociateWebACL
- wafv2:DisassociateWebACL
- shield:GetSubscriptionState
- shield:DescribeProtection
- shield:CreateProtection
- shield:DeleteProtection
Resource: "*"
- Effect: Allow
Action:
- ec2:AuthorizeSecurityGroupIngress
- ec2:RevokeSecurityGroupIngress
Resource: "*"
- Effect: Allow
Action:
- ec2:CreateSecurityGroup
Resource: "*"
- Effect: Allow
Action:
- ec2:CreateTags
Resource: "arn:aws:ec2:*:*:security-group/*"
Condition:
StringEquals:
ec2:CreateAction: CreateSecurityGroup
"Null":
aws:RequestTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- ec2:CreateTags
- ec2:DeleteTags
Resource: "arn:aws:ec2:*:*:security-group/*"
Condition:
"Null":
aws:RequestTag/elbv2.k8s.aws/cluster: "true"
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- ec2:AuthorizeSecurityGroupIngress
- ec2:RevokeSecurityGroupIngress
- ec2:DeleteSecurityGroup
Resource: "*"
Condition:
"Null":
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- elasticloadbalancing:CreateLoadBalancer
- elasticloadbalancing:CreateTargetGroup
Resource: "*"
Condition:
"Null":
aws:RequestTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- elasticloadbalancing:CreateListener
- elasticloadbalancing:DeleteListener
- elasticloadbalancing:CreateRule
- elasticloadbalancing:DeleteRule
Resource: "*"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
- elasticloadbalancing:RemoveTags
Resource:
- "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
- "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*"
- "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
Condition:
"Null":
aws:RequestTag/elbv2.k8s.aws/cluster: "true"
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
- elasticloadbalancing:RemoveTags
Resource:
- "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*"
- "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*"
- "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*"
- "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*"
- Effect: Allow
Action:
- elasticloadbalancing:ModifyLoadBalancerAttributes
- elasticloadbalancing:SetIpAddressType
- elasticloadbalancing:SetSecurityGroups
- elasticloadbalancing:SetSubnets
- elasticloadbalancing:DeleteLoadBalancer
- elasticloadbalancing:ModifyTargetGroup
- elasticloadbalancing:ModifyTargetGroupAttributes
- elasticloadbalancing:DeleteTargetGroup
Resource: "*"
Condition:
"Null":
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
Resource:
- "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
- "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*"
- "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
Condition:
StringEquals:
            Action:
              - sts:AssumeRole
              - sts:TagSession
            Resource: "arn:aws:iam::*:role/rapticore-cross-account-*"
          # IAM and EC2 Read-Only (needed by discovery for role enumeration)
          - Effect: Allow
            Action:
              - iam:ListRole*
              - iam:GetRole*
              - ec2:DescribeRegions
            Resource: "*"
          # Cognito Identity (needed for Azure workload identity)
          - Effect: Allow
            Action:
              - cognito-identity:GetOpenIdTokenForDeveloperIdentity
            Resource: !Sub "arn:aws:cognito-identity:${AWS::Region}:${AWS::AccountId}:identitypool/*"

AWS Load Balancer Controller IAM Role (IRSA)

AwsLoadBalancerControllerRole:
Type: AWS::IAM::Role
DependsOn: OidcProvider
Properties:
RoleName: !Sub "${ClusterName}-aws-load-balancer-controller-role"
AssumeRolePolicyDocument:
Fn::Sub:
- |
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${AWS::AccountId}:oidc-provider/${OidcUrl}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"${OidcUrl}:aud": "sts.amazonaws.com",
"${OidcUrl}:sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
}
}
}]
}
- OidcUrl: !Select [1, !Split ["https://", !GetAtt EksCluster.OpenIdConnectIssuerUrl]]
Policies:
- PolicyName: AWSLoadBalancerControllerPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- iam:CreateServiceLinkedRole
Resource: "arn:aws:iam:::role/aws-service-role/elasticloadbalancing.amazonaws.com/AWSServiceRoleForElasticLoadBalancing"
Condition:
StringEquals:
iam:AWSServiceName: elasticloadbalancing.amazonaws.com
- Effect: Allow
Action:
- ec2:DescribeAccountAttributes
- ec2:DescribeAddresses
- ec2:DescribeAvailabilityZones
- ec2:DescribeInternetGateways
- ec2:DescribeVpcs
- ec2:DescribeVpcPeeringConnections
- ec2:DescribeSubnets
- ec2:DescribeSecurityGroups
- ec2:DescribeInstances
- ec2:DescribeNetworkInterfaces
- ec2:DescribeTags
- ec2:DescribeRouteTables
- ec2:GetCoipPoolUsage
- ec2:DescribeCoipPools
- elasticloadbalancing:DescribeLoadBalancers
- elasticloadbalancing:DescribeLoadBalancerAttributes
- elasticloadbalancing:DescribeListeners
- elasticloadbalancing:DescribeListenerAttributes
- elasticloadbalancing:DescribeListenerCertificates
- elasticloadbalancing:DescribeSSLPolicies
- elasticloadbalancing:DescribeRules
- elasticloadbalancing:DescribeTargetGroups
- elasticloadbalancing:DescribeTargetGroupAttributes
- elasticloadbalancing:DescribeTargetHealth
- elasticloadbalancing:DescribeTags
Resource: "
"
- Effect: Allow
Action:
- cognito-idp:DescribeUserPoolClient
- acm:ListCertificates
- acm:DescribeCertificate
- iam:ListServerCertificates
- iam:GetServerCertificate
- waf-regional:GetWebACL
- waf-regional:GetWebACLForResource
- wafv2:GetWebACL
- wafv2:GetWebACLForResource
- shield:GetSubscriptionState
- shield:DescribeProtection
Resource: ""
- Effect: Allow
Action:
- ec2:AuthorizeSecurityGroupIngress
- ec2:RevokeSecurityGroupIngress
Resource: "arn:aws:ec2:
::security-group/"
Condition:
StringEquals:
aws:ResourceTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- ec2:CreateSecurityGroup
Resource: "arn:aws:ec2:::security-group/"
Condition:
StringEquals:
ec2:Vpc: !Sub "arn:aws:ec2:
::vpc/${VpcId}"
- Effect: Allow
Action:
- ec2:CreateTags
Resource: "arn:aws:ec2:
::security-group/"
Condition:
StringEquals:
ec2:CreateAction: CreateSecurityGroup
aws:RequestTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- ec2:CreateTags
- ec2:DeleteTags
Resource: "arn:aws:ec2:::security-group/"
Condition:
StringEquals:
aws:ResourceTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- ec2:DeleteSecurityGroup
Resource: "arn:aws:ec2:
::security-group/"
Condition:
StringEquals:
aws:ResourceTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- elasticloadbalancing:CreateLoadBalancer
- elasticloadbalancing:CreateTargetGroup
Resource: ""
Condition:
StringEquals:
aws:RequestTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- elasticloadbalancing:CreateListener
- elasticloadbalancing:DeleteListener
- elasticloadbalancing:CreateRule
- elasticloadbalancing:DeleteRule
Resource: "
"
Condition:
StringEquals:
aws:ResourceTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
- elasticloadbalancing:RemoveTags
Resource:
- "arn:aws:elasticloadbalancing:::targetgroup//"
- "arn:aws:elasticloadbalancing:::loadbalancer/net//"
- "arn:aws:elasticloadbalancing:::loadbalancer/app//"
Condition:
StringEquals:
aws:ResourceTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
- elasticloadbalancing:RemoveTags
Resource:
- "arn:aws:elasticloadbalancing:::listener/net///"
- "arn:aws:elasticloadbalancing:
::listener/app///"
- "arn:aws:elasticloadbalancing:::listener-rule/net///"
- "arn:aws:elasticloadbalancing:
::listener-rule/app///"
Condition:
StringEquals:
aws:ResourceTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- elasticloadbalancing:ModifyLoadBalancerAttributes
- elasticloadbalancing:SetIpAddressType
- elasticloadbalancing:SetSecurityGroups
- elasticloadbalancing:SetSubnets
- elasticloadbalancing:DeleteLoadBalancer
- elasticloadbalancing:ModifyTargetGroup
- elasticloadbalancing:ModifyTargetGroupAttributes
- elasticloadbalancing:DeleteTargetGroup
Resource: ""
Condition:
StringEquals:
aws:ResourceTag/elbv2.k8s.aws/cluster: "true"
- Effect: Allow
Action:
- elasticloadbalancing:AddTags
Resource:
- "arn:aws:elasticloadbalancing:
::targetgroup//"
- "arn:aws:elasticloadbalancing:
::loadbalancer/net//"
- "arn:aws:elasticloadbalancing:
::loadbalancer/app//*"


---
🤖 Generated by Rapticore

Comment on lines +70 to +141

ClusterEndpoint:
Type: String
Description: EKS cluster API server endpoint (from EksClusterStack)

ClusterCertificateAuthority:
Type: String
Description: EKS cluster certificate authority data, base64 encoded (from EksClusterStack)

ClusterServiceCidr:
Type: String
Default: "10.100.0.0/16"
Description: Kubernetes service IPv4 CIDR (from EksClusterStack)

Resources:
#####
# Launch Template — sets maxPods=110 via nodeadm for AL2023
# Required because prefix delegation increases available IPs but
# the kubelet default max-pods calculation ignores it.
#####
NodeLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: !Ref DiskSize
VolumeType: gp3
Encrypted: true
UserData:
Fn::Base64: !Sub |
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="BOUNDARY"

--BOUNDARY
Content-Type: application/node.eks.aws

---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
cluster:
name: ${ClusterName}
apiServerEndpoint: ${ClusterEndpoint}
certificateAuthority: ${ClusterCertificateAuthority}
cidr: ${ClusterServiceCidr}
kubelet:
config:
maxPods: 110

--BOUNDARY--

#####
# EKS Node Group
#####
EksNodeGroup:
Type: AWS::EKS::Nodegroup
Properties:
ClusterName: !Ref ClusterName
NodeRole: !Ref NodeRoleArn
Subnets: !Ref SubnetIds
ScalingConfig:
DesiredSize: !Ref DesiredSize
MinSize: !Ref MinSize
MaxSize: !Ref MaxSize
InstanceTypes: !Ref InstanceTypes
CapacityType: !Ref CapacityType
AmiType: !Ref AmiType
LaunchTemplate:
Id: !Ref NodeLaunchTemplate
Version: !GetAtt NodeLaunchTemplate.LatestVersionNumber

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: IMDSv1 Enabled on EC2 Launch Template

Severity: High
Lines: 90-121

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0075)

Description

Instance Metadata Service Version 1 is enabled on the EC2 launch template, which poses a security risk. IMDSv1 allows weaker security controls and is deprecated. Disabling it helps protect against potential attacks.

Remediation Summary

Fix Required

  • Add MetadataOptions configuration to the LaunchTemplateData section of the EC2 launch template
  • Set HttpTokens to 'required' to enforce IMDSv2 and disable IMDSv1
  • Set HttpPutResponseHopLimit to '1' to restrict metadata access to the EC2 instance only

Code Pattern

Add the following to LaunchTemplateData (after BlockDeviceMappings or before UserData):

MetadataOptions:
HttpTokens: required
HttpPutResponseHopLimit: 1

This enforces IMDSv2 (token-based authentication) and prevents IMDSv1 access entirely.

Verification

Confirm the launch template now requires IMDSv2 tokens by checking that HttpTokens is set to 'required' and HttpPutResponseHopLimit is set to 1.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
ClusterEndpoint:
Type: String
Description: EKS cluster API server endpoint (from EksClusterStack)
ClusterCertificateAuthority:
Type: String
Description: EKS cluster certificate authority data, base64 encoded (from EksClusterStack)
ClusterServiceCidr:
Type: String
Default: "10.100.0.0/16"
Description: Kubernetes service IPv4 CIDR (from EksClusterStack)
Resources:
#####
# Launch Template — sets maxPods=110 via nodeadm for AL2023
# Required because prefix delegation increases available IPs but
# the kubelet default max-pods calculation ignores it.
#####
NodeLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: !Ref DiskSize
VolumeType: gp3
Encrypted: true
UserData:
Fn::Base64: !Sub |
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: application/node.eks.aws
---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
cluster:
name: ${ClusterName}
apiServerEndpoint: ${ClusterEndpoint}
certificateAuthority: ${ClusterCertificateAuthority}
cidr: ${ClusterServiceCidr}
kubelet:
config:
maxPods: 110
--BOUNDARY--
#####
# EKS Node Group
#####
EksNodeGroup:
Type: AWS::EKS::Nodegroup
Properties:
ClusterName: !Ref ClusterName
NodeRole: !Ref NodeRoleArn
Subnets: !Ref SubnetIds
ScalingConfig:
DesiredSize: !Ref DesiredSize
MinSize: !Ref MinSize
MaxSize: !Ref MaxSize
InstanceTypes: !Ref InstanceTypes
CapacityType: !Ref CapacityType
AmiType: !Ref AmiType
LaunchTemplate:
Id: !Ref NodeLaunchTemplate
Version: !GetAtt NodeLaunchTemplate.LatestVersionNumber
```yaml
ClusterEndpoint:
Type: String
Description: EKS cluster API server endpoint (from EksClusterStack)
ClusterCertificateAuthority:
Type: String
Description: EKS cluster certificate authority data, base64 encoded (from EksClusterStack)
ClusterServiceCidr:
Type: String
Default: "10.100.0.0/16"
Description: Kubernetes service IPv4 CIDR (from EksClusterStack)
Resources:
#####
# Launch Template — sets maxPods=110 via nodeadm for AL2023
# Required because prefix delegation increases available IPs but
# the kubelet default max-pods calculation ignores it.
#####
NodeLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
MetadataOptions:
HttpTokens: required
HttpPutResponseHopLimit: 1
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: !Ref DiskSize
VolumeType: gp3
Encrypted: true
UserData:
Fn::Base64: !Sub |
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="BOUNDARY"
--BOUNDARY
Content-Type: application/node.eks.aws
---
apiVersion: node.eks.aws/v1alpha1
kind: NodeConfig
spec:
cluster:
name: ${ClusterName}
apiServerEndpoint: ${ClusterEndpoint}
certificateAuthority: ${ClusterCertificateAuthority}
cidr: ${ClusterServiceCidr}
kubelet:
config:
maxPods: 110
--BOUNDARY--
#####
# EKS Node Group
#####
EksNodeGroup:
Type: AWS::EKS::Nodegroup
Properties:
ClusterName: !Ref ClusterName
NodeRole: !Ref NodeRoleArn
Subnets: !Ref SubnetIds
ScalingConfig:
DesiredSize: !Ref DesiredSize
MinSize: !Ref MinSize
MaxSize: !Ref MaxSize
InstanceTypes: !Ref InstanceTypes
CapacityType: !Ref CapacityType
AmiType: !Ref AmiType
LaunchTemplate:
Id: !Ref NodeLaunchTemplate
Version: !GetAtt NodeLaunchTemplate.LatestVersionNumber

---
🤖 Generated by Rapticore

Comment on lines +161 to +212
Properties:
Queues:
- !Ref RealTimeThreatAlertTriggerQueue
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sqs:SendMessage
Resource: !GetAtt RealTimeThreatAlertTriggerQueue.Arn
Condition:
ArnEquals:
aws:SourceArn: !GetAtt RealTimeThreatMonitoringRule.Arn

#####
# S3 Bucket resources
#####

# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId

#####
# SQS Queue resources
#####

# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: S3 Access Logging Not Enabled

Severity: Medium
Lines: 181-192

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0006)

Description

The S3 bucket 'vcs-content-${TenantId}' lacks enabled access logging, which is critical for tracking requests and ensuring auditability. Without it, tracking unauthorized access becomes difficult, potentially leading to undetected breaches.

Remediation Summary

Fix Required

  • Add LoggingConfiguration property to the VcsContentBucket S3 bucket resource (lines 181-192)
  • Specify a target S3 bucket for storing access logs and an optional log file prefix
  • The target bucket must exist and have proper permissions to receive logs

Code Pattern

Add the following to the Properties section of VcsContentBucket:

LoggingConfiguration:
  DestinationBucketName: !Ref LoggingBucket
  LogFilePrefix: "vcs-content-logs/"

Alternatively, reference an existing logging bucket name directly as a string value for DestinationBucketName.

Verification

Verify S3 access logging is enabled by checking that the LoggingConfiguration property is present with a valid DestinationBucketName in the deployed bucket properties.

Priority

medium - Estimated effort: 10 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Properties:
Queues:
- !Ref RealTimeThreatAlertTriggerQueue
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sqs:SendMessage
Resource: !GetAtt RealTimeThreatAlertTriggerQueue.Arn
Condition:
ArnEquals:
aws:SourceArn: !GetAtt RealTimeThreatMonitoringRule.Arn
#####
# S3 Bucket resources
#####
# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId
#####
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
```yaml
Properties:
Queues:
- !Ref RealTimeThreatAlertTriggerQueue
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sqs:SendMessage
Resource: !GetAtt RealTimeThreatAlertTriggerQueue.Arn
Condition:
ArnEquals:
aws:SourceArn: !GetAtt RealTimeThreatMonitoringRule.Arn
#####
# S3 Bucket resources
#####
# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LoggingConfiguration:
DestinationBucketName: !Ref VcsContentBucketLoggingBucket
LogFilePrefix: "vcs-content-logs/"
Tags:
- Key: TenantId
Value: !Ref TenantId
VcsContentBucketLoggingBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-logs-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId
#####
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

---
🤖 Generated by Rapticore

Comment on lines +179 to +223

# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId

#####
# SQS Queue resources
#####

# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Data

Severity: High
Lines: 199-203

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue does not enforce encryption on stored data, posing a risk of unauthorized data access. Encrypting data at rest is critical to protect sensitive information from potential breaches.

Remediation Summary

Fix Required

• Add KmsMasterKeyId property to the SQS queue resource to enable encryption at rest
• Use either an AWS managed key (alias/aws/sqs) or a customer managed KMS key ARN
• Apply this change to lines 199-203 in standard/StandardStack.yaml

Code Pattern

Add the following property to the RapticoreVulnerabilityDLQ resource Properties section:

KmsMasterKeyId: alias/aws/sqs

Alternatively, use a customer managed key:

KmsMasterKeyId: !GetAtt MyKmsKey.Arn

Verification

Deploy the stack and verify in AWS console that the SQS queue's Encryption section shows an active KMS key (not "Not encrypted").

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId
#####
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
```yaml
# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId
#####
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:

---
🤖 Generated by Rapticore

Comment on lines +161 to +212
Properties:
Queues:
- !Ref RealTimeThreatAlertTriggerQueue
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sqs:SendMessage
Resource: !GetAtt RealTimeThreatAlertTriggerQueue.Arn
Condition:
ArnEquals:
aws:SourceArn: !GetAtt RealTimeThreatMonitoringRule.Arn

#####
# S3 Bucket resources
#####

# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId

#####
# SQS Queue resources
#####

# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: S3 Bucket Missing Versioning

Severity: High
Lines: 181-192

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0005)

Description

The S3 bucket lacks versioning, which is critical for maintaining previous versions of objects and mitigating accidental deletions or modifications. This absence increases the risk of data loss and integrity issues.

Remediation Summary

Fix Required

  • Add VersioningConfiguration property to the S3 bucket resource (VcsContentBucket) at lines 181-192
  • Set Status to 'Enabled' under VersioningConfiguration to enable versioning on the bucket
  • This prevents accidental data loss by maintaining object version history

Code Pattern

Add the following property to the Properties section of the S3 bucket:

VersioningConfiguration:
  Status: Enabled

Insert after PublicAccessBlockConfiguration and before Tags for proper YAML structure.

Verification

Deploy the updated CloudFormation template and verify in AWS S3 console that the bucket shows versioning status as 'Enabled' in the bucket properties.

Priority

medium - Estimated effort: 2 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Properties:
Queues:
- !Ref RealTimeThreatAlertTriggerQueue
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sqs:SendMessage
Resource: !GetAtt RealTimeThreatAlertTriggerQueue.Arn
Condition:
ArnEquals:
aws:SourceArn: !GetAtt RealTimeThreatMonitoringRule.Arn
#####
# S3 Bucket resources
#####
# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId
#####
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
```yaml
Properties:
Queues:
- !Ref RealTimeThreatAlertTriggerQueue
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sqs:SendMessage
Resource: !GetAtt RealTimeThreatAlertTriggerQueue.Arn
Condition:
ArnEquals:
aws:SourceArn: !GetAtt RealTimeThreatMonitoringRule.Arn
#####
# S3 Bucket resources
#####
# VCS S3 Bucket
VcsContentBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "vcs-content-${TenantId}"
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId
#####
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

---
🤖 Generated by Rapticore

Comment on lines +185 to +232
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId

#####
# SQS Queue resources
#####

# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Queue Data

Severity: High
Lines: 205-212

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

Data stored in the SQS queue is not encrypted, leading to potential data exposure. Unencrypted data poses a significant security risk, as sensitive information may be intercepted during transmission or while at rest.

Remediation Summary

Fix Required

• Add KmsMasterKeyId property to the RapticoreVulnerabilityQueue resource (lines 205-212) to enable server-side encryption with AWS KMS
• Set the KmsMasterKeyId to either an alias (e.g., 'alias/aws/sqs') for AWS-managed keys or a custom KMS key ARN for customer-managed encryption
• Also apply the same KmsMasterKeyId to the RapticoreVulnerabilityDLQ resource to ensure dead-letter queue messages are encrypted

Code Pattern

Add to the Properties section:

KmsMasterKeyId: !Sub 'arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/sqs'

Or for customer-managed keys, reference your KMS key ARN or alias directly.

Verification

Deploy the updated CloudFormation template and confirm in the AWS SQS console that both queues show 'Enabled' status under Encryption section.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId
#####
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
```yaml
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Tags:
- Key: TenantId
Value: !Ref TenantId
#####
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue

---
🤖 Generated by Rapticore

Comment on lines +195 to +238
# SQS Queue resources
#####

# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted Data in SQS Queue

Severity: High
Lines: 215-219

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue is not configured to encrypt stored data, which poses a risk of data exposure. Encrypting the data ensures that it remains secure at rest, preventing unauthorized access.

Remediation Summary

Fix Required

• Add KmsMasterKeyId property to the SQS Queue resource (lines 215-219) to enable Server-Side Encryption (SSE)
• Set KmsMasterKeyId to reference an AWS KMS key (use alias/aws/sqs for AWS-managed key, or a customer-managed key ARN)
• This ensures messages at rest are encrypted and inaccessible without proper key permissions

Code Pattern

Add the following property to the Properties section:
KmsMasterKeyId: "alias/aws/sqs"

Full corrected resource:
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600
KmsMasterKeyId: "alias/aws/sqs"

Verification

Confirm the deployed SQS queue shows 'Server-side encryption (SSE-KMS)' enabled in AWS console, or verify via AWS CLI: aws sqs get-queue-attributes --queue-url --attribute-names All | grep KmsMasterKeyId

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
# SQS Queue resources
#####
# Rapticore Vulnerability Queue
RapticoreVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue

🤖 Generated by Rapticore

Comment on lines +201 to +248
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3

# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Data Storage

Severity: High
Lines: 221-228

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue 'rapticore-container-vulnerability' is not configured to encrypt data at rest, leaving it susceptible to unauthorized access. Encrypting data in transit and at rest is crucial for maintaining data integrity and confidentiality.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the SQS queue to enable server-side encryption using AWS KMS
  • Apply the same encryption configuration to the associated Dead Letter Queue (RapticoreContainerVulnerabilityDLQ) for consistency
  • Specify a KMS key ARN or alias to encrypt messages at rest

Code Pattern

Add to the Properties section of AWS::SQS::Queue:

KmsMasterKeyId: alias/aws/sqs

Or use a custom KMS key ARN for enhanced key management:

KmsMasterKeyId: !GetAtt SQSEncryptionKey.Arn

Verification

Deploy the stack and verify in AWS Console that the SQS queue shows Encryption status as 'Enabled' and displays the KMS key ID.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
```yaml
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue

---
🤖 Generated by Rapticore

Comment on lines +211 to +255
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3

# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Queue Data

Severity: High
Lines: 231-235

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue is storing data without encryption, which can lead to unauthorized data access. This poses a significant security risk as plaintext data can be intercepted and read by malicious actors.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the SQS queue definition at lines 231-235 in StandardStack.yaml
  • Specify a KMS key ARN or alias for server-side encryption (SSE-SQS)
  • Apply the same encryption configuration to any related SQS queues (main queue, if separate)

Code Pattern

Add to the Properties section:

KmsMasterKeyId: !Sub "arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/YOUR-KEY-ID"

Or use AWS managed key:

KmsMasterKeyId: alias/aws/sqs

Verification

Deploy the stack and verify in AWS Console that the SQS queue shows "Encryption" enabled with the specified KMS key.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
```yaml
deadLetterTargetArn: !GetAtt RapticoreVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Container Vulnerability Queue
RapticoreContainerVulnerabilityDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:

---
🤖 Generated by Rapticore

Comment on lines +227 to +271
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3

# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: SQS Queue Data Encryption Missing

Severity: High
Lines: 247-251

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue is not configured to encrypt stored data, which can lead to unauthorized data access. Encrypting data at rest is crucial for maintaining data confidentiality and integrity.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the SQS Queue resource (lines 247-251) to enable server-side encryption
  • Use AWS managed key (alias/aws/sqs) or specify a custom KMS key ARN for encryption
  • This ensures all messages stored in the DLQ are encrypted at rest

Code Pattern

Add the following property to the Properties section:

KmsMasterKeyId: alias/aws/sqs

Or use a custom KMS key ARN:

KmsMasterKeyId: arn:aws:kms:region:account-id:key/key-id

Verification

Deploy the updated CloudFormation template and verify the SQS queue has encryption enabled by checking the queue attributes in the AWS Console or via CLI (aws sqs get-queue-attributes --queue-url --attribute-names All).

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
```yaml
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:

---
🤖 Generated by Rapticore

Comment on lines +217 to +264
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3

# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3

# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Queue Data

Severity: High
Lines: 237-244

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

Data stored in the SQS queue is not encrypted, posing a risk of unauthorized access to sensitive information. This misconfiguration can lead to data breaches and compliance violations.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the SQS Queue resource (lines 237-244) to enable server-side encryption
  • Use AWS managed key alias/aws/sqs or specify a customer managed KMS key ARN
  • Apply the same encryption to the dead-letter queue (RapticoreResourceVulnerabilitySummaryDLQ) for consistency

Code Pattern

Add to the Properties section of RapticoreResourceVulnerabilitySummaryQueue:

KmsMasterKeyId: alias/aws/sqs

For customer-managed keys, use:

KmsMasterKeyId: !GetAtt MyKmsKey.Arn

Verification

Confirm the CloudFormation stack deploys successfully and verify in AWS Console that the SQS queue shows encryption enabled under Queue Details.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
```yaml
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreContainerVulnerabilityQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-container-vulnerability-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreContainerVulnerabilityDLQ.Arn
maxReceiveCount: 3
# Rapticore Resource Vulnerability Summary Queue
RapticoreResourceVulnerabilitySummaryDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue

---
🤖 Generated by Rapticore

Comment on lines +233 to +280
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3

# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: SQS Queue Data Not Encrypted

Severity: High
Lines: 253-260

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

Data stored in the SQS queue is not encrypted, which poses a risk of unauthorized data access. Encrypting the data ensures that it remains protected at rest, mitigating potential data breaches.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the SQS Queue resource to enable server-side encryption
  • Set the value to a valid AWS KMS key ARN or use alias format (e.g., 'alias/aws/sqs' for AWS managed key)
  • Apply the same encryption to the associated Dead Letter Queue (RapticoreReactiveRemediationDLQ) for consistency

Code Pattern

Add to the Properties section of RapticoreReactiveRemediationQueue (after line 257):

KmsMasterKeyId: alias/aws/sqs

Alternatively, reference a custom KMS key: !GetAtt MyKmsKey.Arn

Verification

Deploy the updated CloudFormation template and verify in AWS Console that the queue has 'Server-side encryption (SSE-SQS)' enabled under Queue Details.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
```yaml
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreResourceVulnerabilitySummaryQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-resource-vulnerability-summary-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue

---
🤖 Generated by Rapticore

Comment on lines +243 to +287
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3

# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Queue Data

Severity: High
Lines: 263-267

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue is not configured to encrypt stored data, which could expose sensitive information if intercepted. Encryption is crucial for protecting data at rest against unauthorized access.

Remediation Summary

Fix Required

• Add KmsMasterKeyId property to the SQS Queue resource (lines 263-267) to enable encryption at rest
• Set KmsMasterKeyId to either an AWS managed key (alias/aws/sqs) or a customer managed KMS key ARN
• This ensures all messages stored in the queue are encrypted using the specified KMS key

Code Pattern

Add the following property to the RealTimeThreatAlertTriggerDLQ Properties section:

KmsMasterKeyId: alias/aws/sqs

Or use a customer managed key:

KmsMasterKeyId: arn:aws:kms:region:account-id:key/key-id

Verification

Deploy the stack and verify in AWS Console that the SQS queue shows encryption status as 'Enabled' in the queue details.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Looking at the finding, the issue is that SQS queues are not configured with encryption at rest. I need to add the `KmsMasterKeyId` property to enable encryption for the SQS queues.
The fix needs to be applied to all SQS queues shown in the snippet. Based on the finding location (lines 263-267) which corresponds to the `RealTimeThreatAlertTriggerDLQ`, I need to add encryption to all SQS Queue resources in this snippet range.
deadLetterTargetArn: !GetAtt RapticoreResourceVulnerabilitySummaryDLQ.Arn
maxReceiveCount: 3
# Rapticore Reactive Remediation Queue
RapticoreReactiveRemediationDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue

🤖 Generated by Rapticore

Comment on lines +249 to +296
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3

# VCS Trigger AppSec Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted Data in SQS Queue

Severity: High
Lines: 269-276

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue is not configured to encrypt stored data, posing a risk of data exposure. Encryption ensures that sensitive information remains secure, even if the data is accessed improperly. Unauthorized access to unencrypted data can lead to significant data breaches.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the SQS queue configuration to enable server-side encryption using AWS KMS
  • Specify either a KMS key ARN/ID or use the alias alias/aws/sqs for AWS-managed encryption
  • Apply this change to the RealTimeThreatAlertTriggerQueue resource at lines 269-276

Code Pattern

Add the following property to the Properties section of the SQS queue:

KmsMasterKeyId: alias/aws/sqs

Or use a custom KMS key ARN for enhanced control:

KmsMasterKeyId: !GetAtt SQSEncryptionKey.Arn

Verification

Deploy the updated CloudFormation template and verify in the AWS Console that the SQS queue shows encryption enabled with the specified KMS key.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
```yaml
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RapticoreReactiveRemediationQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "rapticore-reactive-remediation-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue

---
🤖 Generated by Rapticore

Comment on lines +259 to +304
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3

# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: SQS Data Encryption Missing

Severity: High
Lines: 279-284

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue does not have data encryption enabled, which poses a risk of data exposure if the queue messages are intercepted. Encryption ensures that the data remains secure both at rest and in transit.

Remediation Summary

Fix Required

• Add KmsMasterKeyId property to enable server-side encryption for the SQS queue at lines 279-284
• Specify a KMS key ARN or use the alias 'alias/aws/sqs' for AWS-managed encryption
• Apply this fix to the RealTimeThreatAlertTriggerAzureDLQ resource Properties section

Code Pattern

Add the following property to the SQS Queue Properties:

KmsMasterKeyId: alias/aws/sqs

Or use a custom KMS key ARN:

KmsMasterKeyId: arn:aws:kms:region:account-id:key/key-id

Verification

Deploy the CloudFormation template and verify in AWS Console that the SQS queue shows encryption enabled under Queue Details.

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
```yaml
deadLetterTargetArn: !GetAtt RapticoreReactiveRemediationDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Queue (tenant-specific)
RealTimeThreatAlertTriggerDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue

---
🤖 Generated by Rapticore

Comment on lines +266 to +314
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3

# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3

# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3

# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted Data in SQS Queue

Severity: High
Lines: 286-294

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

Data stored in the SQS queue is not encrypted, posing a risk of unauthorized data access. Unencrypted data could be intercepted and read by malicious actors, leading to potential data breaches.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the RealTimeThreatAlertTriggerAzureQueue resource (lines 286-294) to enable server-side encryption
  • Reference an AWS KMS key ARN or use 'alias/aws/sqs' for AWS-managed encryption
  • Apply the same encryption to the associated dead-letter queue (RealTimeThreatAlertTriggerAzureDLQ) for consistency

Code Pattern

Add to Properties section:

KmsMasterKeyId: alias/aws/sqs

Or reference a custom KMS key:

KmsMasterKeyId: !GetAtt SQSKey.Arn

Verification

Deploy the stack and verify in AWS Console that the SQS queue properties show 'Encryption' is enabled with the specified KMS key.

Priority

high - Estimated effort: 10 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
```yaml
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerDLQ.Arn
maxReceiveCount: 3
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue

---
🤖 Generated by Rapticore

Comment on lines +277 to +321

# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3

# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3

# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Queue Data

Severity: High
Lines: 297-301

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue data is not encrypted, which poses a security risk. Data in transit and at rest should be encrypted to prevent unauthorized access. Unencrypted data could lead to data breaches and compliance violations.

Remediation Summary

Fix Required

• Add KmsMasterKeyId property to the SQS queue definition to enable server-side encryption
• Use AWS managed key (alias/aws/sqs) or a customer managed KMS key ARN
• Apply this fix to the VcsTriggerAppSecDLQ resource at lines 297-301

Code Pattern

Add the following property within the Properties section:

KmsMasterKeyId: alias/aws/sqs

Or for customer managed keys:

KmsMasterKeyId: arn:aws:kms:region:account-id:key/key-id

Verification

Deploy the stack and verify the SQS queue has encryption enabled by checking AWS Console or CLI: aws sqs get-queue-attributes --queue-url <queue-url> --attribute-names All | grep KmsMasterKeyId

Priority

high - Estimated effort: 5 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
```yaml
# Real-time Threat Alert Trigger Azure Queue (Conditional)
RealTimeThreatAlertTriggerAzureDLQ:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:

---
🤖 Generated by Rapticore

Comment on lines +283 to +330
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3

# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3

# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3

#####
# Azure Workload Identity Resources
#####

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Queue Data

Severity: High
Lines: 303-310

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue 'vcs-trigger-appsec-${TenantId}' is not configured to encrypt data at rest. This poses a risk of unauthorized access to sensitive information if the data is intercepted.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the VcsTriggerAppSecQueue resource (lines 303-310) to enable encryption at rest
  • Reference an existing KMS key ARN or create a new AWS::KMS::Key resource for SQS encryption
  • Apply the same encryption to the dead-letter queue (VcsTriggerAppSecDLQ) to maintain consistent security

Code Pattern

Add the following property under Properties in the VcsTriggerAppSecQueue resource:
KmsMasterKeyId: !GetAtt SqsEncryptionKey.Arn

Or use AWS managed key:
KmsMasterKeyId: alias/aws/sqs

If creating a dedicated key, add before the queue resource:
SqsEncryptionKey:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Enable IAM policies
Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: 'kms:'
Resource: '
'
- Sid: Allow SQS to use the key
Effect: Allow
Principal:
Service: sqs.amazonaws.com
Action:
- 'kms:Decrypt'
- 'kms:GenerateDataKey'
Resource: '*'

Verification

Deploy the CloudFormation stack and verify in AWS Console that the SQS queue shows an encryption key configured under the encryption settings, or use AWS CLI: aws sqs get-queue-attributes --queue-url --attribute-names KmsMasterKeyId

Priority

high - Estimated effort: 10-15 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3
#####
# Azure Workload Identity Resources
#####
```yaml
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
RealTimeThreatAlertTriggerAzureQueue:
Type: AWS::SQS::Queue
Condition: HasAzureTenant
Properties:
QueueName: !Sub "real-time-threat-alert-trigger-azure-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3
#####
# Azure Workload Identity Resources
#####

---
🤖 Generated by Rapticore

Comment on lines +293 to +326
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3

# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3

# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: SQS Queue Data Encryption Not Enabled

Severity: High
Lines: 313-317

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue is not configured to encrypt data at rest. This allows unauthorized access to sensitive information stored in the queue, potentially leading to data breaches. It is crucial to encrypt data to protect it from unauthorized access.

Remediation Summary

Fix Required

• Add KmsMasterKeyId property to the SQS Queue resource (lines 313-317) to enable server-side encryption
• Reference an AWS KMS key (either create a new one or use an existing ARN) for encrypting queue messages at rest
• Apply this fix to all SQS Queue resources in the CloudFormation template that handle sensitive data

Code Pattern

Add the following property to the VcsTriggerPostScanDLQ resource Properties section:

KmsMasterKeyId: !GetAtt SQSEncryptionKey.Arn

Alternatively, use AWS managed key:

KmsMasterKeyId: alias/aws/sqs

Verification

Deploy the updated CloudFormation stack and verify in AWS Console that the SQS queue shows "Encryption" as "Enabled" with the KMS key ARN displayed.

Priority

high - Estimated effort: 10 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3
deadLetterTargetArn: !GetAtt RealTimeThreatAlertTriggerAzureDLQ.Arn
maxReceiveCount: 3
# VCS Trigger AppSec Queue
VcsTriggerAppSecDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
KmsMasterKeyId: alias/aws/sqs
VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3

🤖 Generated by Rapticore

Comment on lines +299 to +346
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3

# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days

VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3

#####
# Azure Workload Identity Resources
#####
AzureWorkloadIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: !Join
- ""
- - "azure_workload_identity_pool_"
- !Select [0, !Split [".", !Ref Domain]]
AllowUnauthenticatedIdentities: false
DeveloperProviderName: !Join
- ""
- - "azure_workload_identity_"
- !Select [0, !Split [".", !Ref Domain]]

LambdaPermission:
Type: AWS::Lambda::Permission
Properties:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted SQS Queue Data

Severity: High
Lines: 319-326

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0115)

Description

The SQS queue is not configured to encrypt data at rest, which poses a risk of data exposure. Encrypting queue data is a critical security measure to protect sensitive information from unauthorized access.

Remediation Summary

Fix Required

  • Add KmsMasterKeyId property to the VcsTriggerPostScanQueue resource (lines 319-326) to enable server-side encryption with AWS KMS
  • Use either a specific KMS key ARN or the alias 'alias/aws/sqs' for AWS-managed encryption
  • Apply the same encryption configuration to the associated Dead Letter Queue (VcsTriggerPostScanDLQ) for consistency

Code Pattern

Add the following to the Properties section of AWS::SQS::Queue:

KmsMasterKeyId: alias/aws/sqs

For customer-managed keys, use the full ARN:

KmsMasterKeyId: !GetAtt SQSEncryptionKey.Arn

Verification

Deploy the updated stack and verify in AWS Console that both SQS queues show 'Server-side encryption (SSE-SQS)' or 'Server-side encryption (SSE-KMS)' enabled under queue settings.

Priority

high - Estimated effort: 10 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3
#####
# Azure Workload Identity Resources
#####
AzureWorkloadIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: !Join
- ""
- - "azure_workload_identity_pool_"
- !Select [0, !Split [".", !Ref Domain]]
AllowUnauthenticatedIdentities: false
DeveloperProviderName: !Join
- ""
- - "azure_workload_identity_"
- !Select [0, !Split [".", !Ref Domain]]
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
```yaml
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerAppSecQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-appsec-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerAppSecDLQ.Arn
maxReceiveCount: 3
# VCS Trigger Post Scan Queue
VcsTriggerPostScanDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}-dlq"
MessageRetentionPeriod: 1209600 # 14 days
VcsTriggerPostScanQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "vcs-trigger-post-scan-${TenantId}"
MessageRetentionPeriod: 43200 # 12 hours
KmsMasterKeyId: alias/aws/sqs
RedrivePolicy:
deadLetterTargetArn: !GetAtt VcsTriggerPostScanDLQ.Arn
maxReceiveCount: 3
#####
# Azure Workload Identity Resources
#####
AzureWorkloadIdentityPool:
Type: AWS::Cognito::IdentityPool
Properties:
IdentityPoolName: !Join
- ""
- - "azure_workload_identity_pool_"
- !Select [0, !Split [".", !Ref Domain]]
AllowUnauthenticatedIdentities: false
DeveloperProviderName: !Join
- ""
- - "azure_workload_identity_"
- !Select [0, !Split [".", !Ref Domain]]
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:

---
🤖 Generated by Rapticore

Comment on lines +352 to +401
# Custom Resource that uses the existing Lambda function
GetOpenIdTokenCustomResource:
Type: Custom::GetOpenIdToken
DependsOn:
- LambdaPermission
Properties:
ServiceToken: !Ref AzureIdentityLambdaArn
IdentityPoolId: !Ref AzureWorkloadIdentityPool
#####
# Login Key should be same as developer provider name
#####
LoginKey: !Join
- ""
- - "azure_workload_identity_"
- !Select [0, !Split [".", !Ref Domain]]
LoginValue: !Select [0, !Split [".", !Ref Domain]]

#####
# CRS Cross-Account Authentication Resources
#####
CrsServiceUserPassword:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub "CrsServiceUserPassword-${TenantId}"
GenerateSecretString:
SecretStringTemplate: !Sub '{"username": "crs-service-${TenantId}@rapticore.com"}'
GenerateStringKey: password
ExcludeCharacters: '"@/\'
PasswordLength: 32
RequireEachIncludedType: true

CrsUserManagementRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted Secrets Manager Secret

Severity: High
Lines: 372-381

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0116)

Description

A Secrets Manager secret is not encrypted using a KMS CMK, which poses a security risk. Without encryption, sensitive data could be exposed if the secret storage is compromised. It is crucial to encrypt secrets to prevent unauthorized access.

Remediation Summary

Fix Required

  • Add KmsKeyId property to the AWS::SecretsManager::Secret resource to enable encryption with a KMS Customer Master Key (CMK)
  • Reference an existing KMS CMK ARN or create a new one in the CloudFormation template
  • Ensure the KMS key policy allows the Secrets Manager service and relevant IAM roles to decrypt the secret

Code Pattern

Add the KmsKeyId property under Properties of the CrsServiceUserPassword resource:

Properties:
  Name: !Sub "CrsServiceUserPassword-${TenantId}"
  KmsKeyId: !GetAtt SecretsManagerKmsKey.Arn
  GenerateSecretString:
    ...

If a KMS key doesn't exist, create one:

SecretsManagerKmsKey:
  Type: AWS::KMS::Key
  Properties:
    KeyPolicy:
      Statement:
        - Sid: Enable IAM policies
          Effect: Allow
          Principal:
            AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
          Action: 'kms:*'
          Resource: '*'
        - Sid: Allow Secrets Manager
          Effect: Allow
          Principal:
            Service: secrets.amazonaws.com
          Action:
            - 'kms:Decrypt'
            - 'kms:GenerateDataKey'
          Resource: '*'

Verification

Deploy the stack and verify the secret's encryption status in AWS Secrets Manager console shows it is encrypted with the specified KMS key.

Priority

high - Estimated effort: 15 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
# Custom Resource that uses the existing Lambda function
GetOpenIdTokenCustomResource:
Type: Custom::GetOpenIdToken
DependsOn:
- LambdaPermission
Properties:
ServiceToken: !Ref AzureIdentityLambdaArn
IdentityPoolId: !Ref AzureWorkloadIdentityPool
#####
# Login Key should be same as developer provider name
#####
LoginKey: !Join
- ""
- - "azure_workload_identity_"
- !Select [0, !Split [".", !Ref Domain]]
LoginValue: !Select [0, !Split [".", !Ref Domain]]
#####
# CRS Cross-Account Authentication Resources
#####
CrsServiceUserPassword:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub "CrsServiceUserPassword-${TenantId}"
GenerateSecretString:
SecretStringTemplate: !Sub '{"username": "crs-service-${TenantId}@rapticore.com"}'
GenerateStringKey: password
ExcludeCharacters: '"@/\'
PasswordLength: 32
RequireEachIncludedType: true
CrsUserManagementRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
```yaml
CrsServiceUserPassword:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub "CrsServiceUserPassword-${TenantId}"
KmsKeyId: !Ref SecretsManagerKmsKey
GenerateSecretString:
SecretStringTemplate: !Sub '{"username": "crs-service-${TenantId}@rapticore.com"}'
GenerateStringKey: password
ExcludeCharacters: '"@/\'
PasswordLength: 32
RequireEachIncludedType: true

---
🤖 Generated by Rapticore

Comment on lines +389 to +532
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword

CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
Code:
ZipFile: |
import boto3
import json
import cfnresponse

def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']

assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)

# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)

secrets = boto3.client('secretsmanager')

user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']

if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)

# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")

if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']

# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)

# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)

# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group

cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})

elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"

CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Lambda Function Missing Dead Letter Queue Configuration

Severity: Low
Lines: 409-512

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0028)

Description

The AWS Lambda function is not configured for a Dead Letter Queue (DLQ). This can result in lost events if the Lambda function fails to process them. A DLQ helps manage these failures by capturing and storing them for later processing.

Remediation Summary

Fix Required

  • Add a Dead Letter Queue (DLQ) configuration to the Lambda function by creating an SQS queue resource and referencing it in the Lambda's DeadLetterConfig property
  • Create an AWS::SQS::Queue resource (e.g., CrsUserManagementDLQ) in the CloudFormation template
  • Add DeadLetterConfig property to CrsUserManagementFunction (after line 415, within Properties) pointing to the SQS queue ARN

Code Pattern

Add SQS queue resource:

  CrsUserManagementDLQ:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: !Sub "CrsUserManagement-DLQ-${TenantId}"
      MessageRetentionPeriod: 1209600

Add to Lambda Properties (after Timeout property):

      DeadLetterConfig:
        TargetArn: !GetAtt CrsUserManagementDLQ.Arn

Ensure Lambda execution role has SQS SendMessage permissions on the DLQ.

Verification

Validate the CloudFormation template and confirm the Lambda function's DeadLetterConfig points to a valid SQS queue ARN.

Priority

medium - Estimated effort: 10 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword
CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
Code:
ZipFile: |
import boto3
import json
import cfnresponse
def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']
assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)
# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
secrets = boto3.client('secretsmanager')
user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']
if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)
# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")
if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']
# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)
# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)
# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group
cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})
elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"
CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
```yaml
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword
CrsUserManagementDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "CrsUserManagement-DLQ-${TenantId}"
MessageRetentionPeriod: 1209600
CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
DeadLetterConfig:
TargetArn: !GetAtt CrsUserManagementDLQ.Arn
Code:
ZipFile: |
import boto3
import json
import cfnresponse
def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']
assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)
# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
secrets = boto3.client('secretsmanager')
user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']
if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)
# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")
if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']
# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)
# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)
# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group
cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})
elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"
CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",

---
🤖 Generated by Rapticore

Comment on lines +389 to +532
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword

CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
Code:
ZipFile: |
import boto3
import json
import cfnresponse

def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']

assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)

# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)

secrets = boto3.client('secretsmanager')

user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']

if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)

# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")

if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']

# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)

# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)

# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group

cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})

elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"

CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Lambda concurrent execution limit not set

Severity: Low
Lines: 409-512

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0031)

Description

The AWS Lambda function is not configured with a function-level concurrent execution limit, which could lead to resource exhaustion. Setting this limit helps manage concurrency and prevent potential abuse or unexpected costs.

Remediation Summary

Fix Required

  • Add ReservedConcurrentExecutions property to the Lambda function's Properties section to set a function-level concurrent execution limit
  • This property should be set to an appropriate numeric value (e.g., 10, 100) based on your workload requirements to prevent resource exhaustion and uncontrolled scaling
  • Place this property at the same indentation level as other Properties like Runtime, Handler, Role, and Timeout

Code Pattern

Add the following line to the CrsUserManagementFunction Properties (after line 413, alongside other properties):

      ReservedConcurrentExecutions: 10

Adjust the value based on expected concurrency requirements for this tenant-specific function.

Verification

Confirm the Lambda function definition includes the ReservedConcurrentExecutions property and redeploy the CloudFormation stack to apply the concurrent execution limit.

Priority

low - Estimated effort: 2 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword
CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
Code:
ZipFile: |
import boto3
import json
import cfnresponse
def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']
assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)
# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
secrets = boto3.client('secretsmanager')
user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']
if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)
# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")
if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']
# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)
# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)
# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group
cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})
elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"
CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
```yaml
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword
CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
ReservedConcurrentExecutions: 10
Code:
ZipFile: |
import boto3
import json
import cfnresponse
def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']
assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)
# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
secrets = boto3.client('secretsmanager')
user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']
if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)
# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")
if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']
# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)
# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)
# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group
cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})
elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"
CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",

---
🤖 Generated by Rapticore

Comment on lines +389 to +532
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword

CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
Code:
ZipFile: |
import boto3
import json
import cfnresponse

def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']

assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)

# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)

secrets = boto3.client('secretsmanager')

user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']

if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)

# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")

if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']

# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)

# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)

# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group

cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})

elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"

CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Lambda Function Not Configured in VPC

Severity: Medium
Lines: 409-512

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0026)

Description

AWS Lambda function is not configured within a Virtual Private Cloud (VPC). This can expose the function to external networks, increasing the risk of unauthorized access. Proper VPC configuration is essential for network security and compliance.

Remediation Summary

Fix Required

  • Add VpcConfig property to the Lambda function definition (after line 414, after the Role property)
  • Reference existing VPC subnets and security groups from the CloudFormation stack (use !GetAtt or !Ref to existing resources)
  • Ensure the Lambda execution role has permissions for ec2:CreateNetworkInterface, ec2:DescribeNetworkInterfaces, and ec2:DeleteNetworkInterface

Code Pattern

Add the following YAML block to the CrsUserManagementFunction Properties section:

VpcConfig:
  SecurityGroupIds:
    - !Ref LambdaSecurityGroup
  SubnetIds:
    - !Ref PrivateSubnet1
    - !Ref PrivateSubnet2

Alternatively, reference existing resources:

VpcConfig:
  SecurityGroupIds:
    - sg-xxxxxxxx
  SubnetIds:
    - subnet-xxxxxxxx
    - subnet-xxxxxxxx

Verification

Deploy the CloudFormation stack and verify in AWS Lambda console that the function shows VPC configuration and network interfaces are attached.

Priority

medium - Estimated effort: 10 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword
CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
Code:
ZipFile: |
import boto3
import json
import cfnresponse
def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']
assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)
# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
secrets = boto3.client('secretsmanager')
user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']
if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)
# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")
if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']
# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)
# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)
# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group
cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})
elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"
CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
```yaml
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
Policies:
- PolicyName: CognitoUserManagement
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Ref CognitoManagementRoleArn
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref CrsServiceUserPassword
CrsUserManagementFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "CrsUserManagement-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt CrsUserManagementRole.Arn
Timeout: 60
VpcConfig:
SecurityGroupIds:
- !Ref LambdaSecurityGroup
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
Code:
ZipFile: |
import boto3
import json
import cfnresponse
def handler(event, context):
try:
# Assume cross-account role first
sts = boto3.client('sts')
cognito_mgmt_role_arn = event['ResourceProperties']['CognitoManagementRoleArn']
assumed_role = sts.assume_role(
RoleArn=cognito_mgmt_role_arn,
RoleSessionName='cognito-user-management'
)
# Create Cognito client with assumed role credentials
cognito = boto3.client(
'cognito-idp',
region_name='us-west-2',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken']
)
secrets = boto3.client('secretsmanager')
user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
group_name = event['ResourceProperties']['GroupName']
password_secret_arn = event['ResourceProperties']['PasswordSecretArn']
if event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
# Check if group exists, create if not
try:
cognito.get_group(GroupName=group_name, UserPoolId=user_pool_id)
print(f"Group {group_name} already exists")
except cognito.exceptions.ResourceNotFoundException:
print(f"Creating group {group_name}")
cognito.create_group(
GroupName=group_name,
UserPoolId=user_pool_id,
Description=f"CRS tenant group for {group_name}"
)
# Check if user exists
user_exists = False
try:
cognito.admin_get_user(UserPoolId=user_pool_id, Username=username)
user_exists = True
print(f"User {username} already exists")
except cognito.exceptions.UserNotFoundException:
print(f"User {username} does not exist, creating")
if not user_exists:
# Get password from secret
secret = secrets.get_secret_value(SecretId=password_secret_arn)
password_data = json.loads(secret['SecretString'])
password = password_data['password']
# Create user
cognito.admin_create_user(
UserPoolId=user_pool_id,
Username=username,
MessageAction='SUPPRESS',
TemporaryPassword='TempPassword123!'
)
# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)
# Always ensure user is in group
try:
cognito.admin_add_user_to_group(
UserPoolId=user_pool_id,
Username=username,
GroupName=group_name
)
except cognito.exceptions.UserNotConfirmedException:
pass # User already in group
cfnresponse.send(event, context, cfnresponse.SUCCESS, {'Username': username})
elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"
CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",

---
🤖 Generated by Rapticore

Comment on lines +505 to +562

elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})

except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"

CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
"aws_region": "us-west-2",
"user_pool_id": "${SharedUserPoolId}",
"client_id": "${SharedUserPoolClientId}",
"service_username": "crs-service-${TenantId}@rapticore.com",
"service_password": "{{resolve:secretsmanager:${CrsServiceUserPassword}:SecretString:password}}",
"target_account_id": "${AWS::AccountId}",
"api_endpoint": "https://${Domain}/api/v1/ingest/sarif",
"cognito_group": "${CognitoGroupName}",
"cross_account_role_arn": "arn:aws:iam::${CrsAccountId}:role/crs-cognito-access-standard-tenancy"
}
#####
# Queue Deletion Custom Resource
#####
QueueDeletionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Unencrypted Secrets Manager Secret

Severity: High
Lines: 525-542

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0116)

Description

The Secrets Manager secret is not encrypted using a KMS CMK. This poses a security risk as sensitive data could be exposed if the secret is accessed without proper encryption. Unauthorized access may lead to data breaches.

Remediation Summary

Fix Required

  • Add KmsKeyId property to the AWS::SecretsManager::Secret resource (lines 525-542) to specify a customer-managed KMS key for encryption
  • Reference an existing KMS CMK ARN or create a new one if needed; the key must be in the same region
  • Ensure the KMS key policy grants SecretsManager permission to use the key for encryption/decryption operations

Code Pattern

Add to the Properties section of CrsCredentials:

Properties:
  Name: !Sub "CrsCredentials-${TenantId}"
  KmsKeyId: !GetAtt SecretsManagerKmsKey.Arn
  SecretString: !Sub |
    {...}

Alternatively, use an existing CMK ARN directly: KmsKeyId: arn:aws:kms:region:account-id:key/key-id

Verification

Verify the secret is encrypted by checking AWS console or CLI: aws secretsmanager describe-secret --secret-id CrsCredentials-{TenantId} should show a KmsKeyId in the response.

Priority

high - Estimated effort: 10 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"
CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
"aws_region": "us-west-2",
"user_pool_id": "${SharedUserPoolId}",
"client_id": "${SharedUserPoolClientId}",
"service_username": "crs-service-${TenantId}@rapticore.com",
"service_password": "{{resolve:secretsmanager:${CrsServiceUserPassword}:SecretString:password}}",
"target_account_id": "${AWS::AccountId}",
"api_endpoint": "https://${Domain}/api/v1/ingest/sarif",
"cognito_group": "${CognitoGroupName}",
"cross_account_role_arn": "arn:aws:iam::${CrsAccountId}:role/crs-cognito-access-standard-tenancy"
}
#####
# Queue Deletion Custom Resource
#####
QueueDeletionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
```yaml
elif event['RequestType'] == 'Delete':
# Don't delete user - preserve it for reuse
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
print(f"Error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
CreateCrsServiceUser:
Type: Custom::CrsServiceUser
Properties:
ServiceToken: !GetAtt CrsUserManagementFunction.Arn
UserPoolId: !Ref SharedUserPoolId
Username: !Sub "crs-service-${TenantId}@rapticore.com"
GroupName: !Ref CognitoGroupName
PasswordSecretArn: !Ref CrsServiceUserPassword
CognitoManagementRoleArn: !Ref CognitoManagementRoleArn
Timestamp: "2025-08-17-19-11"
CrsCredentials:
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
KmsKeyId: !GetAtt SecretsEncryptionKey.Arn
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
"aws_region": "us-west-2",
"user_pool_id": "${SharedUserPoolId}",
"client_id": "${SharedUserPoolClientId}",
"service_username": "crs-service-${TenantId}@rapticore.com",
"service_password": "{{resolve:secretsmanager:${CrsServiceUserPassword}:SecretString:password}}",
"target_account_id": "${AWS::AccountId}",
"api_endpoint": "https://${Domain}/api/v1/ingest/sarif",
"cognito_group": "${CognitoGroupName}",
"cross_account_role_arn": "arn:aws:iam::${CrsAccountId}:role/crs-cognito-access-standard-tenancy"
}
#####
# Queue Deletion Custom Resource
#####
QueueDeletionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:

---
🤖 Generated by Rapticore

Comment on lines +526 to +571
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
"aws_region": "us-west-2",
"user_pool_id": "${SharedUserPoolId}",
"client_id": "${SharedUserPoolClientId}",
"service_username": "crs-service-${TenantId}@rapticore.com",
"service_password": "{{resolve:secretsmanager:${CrsServiceUserPassword}:SecretString:password}}",
"target_account_id": "${AWS::AccountId}",
"api_endpoint": "https://${Domain}/api/v1/ingest/sarif",
"cognito_group": "${CognitoGroupName}",
"cross_account_role_arn": "arn:aws:iam::${CrsAccountId}:role/crs-cognito-access-standard-tenancy"
}
#####
# Queue Deletion Custom Resource
#####
QueueDeletionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"

QueueDeletionFunction:
Type: AWS::Lambda::Function

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: IAM Policy Allows Unrestricted Write Access

Severity: High
Lines: 546-568

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0019)

Description

The IAM policy allows write access to SQS queues without constraints, which can lead to unauthorized modifications or deletions. This policy grants permissions such as sqs:DeleteQueue without restricting it to specific resources. The lack of constraints exposes the system to potential misuse and data loss.

Remediation Summary

Fix Required

• Replace the overly permissive Resource: "" on lines 563-564 with specific SQS queue ARNs that the Lambda function is authorized to delete
• Separate the sqs:ListQueues action into a separate statement with Resource: "
" if listing all queues is required, keeping destructive actions (sqs:DeleteQueue) restricted to specific resources
• Optionally add conditions to further restrict access (e.g., by queue name pattern or tag-based conditions)

Code Pattern

Replace the Resource line with specific queue ARNs:

Resource:
  - "arn:aws:sqs:REGION:ACCOUNT_ID:QueueName"
  - "arn:aws:sqs:REGION:ACCOUNT_ID:QueueName2"

Or use a pattern for multiple queues:

Resource: "arn:aws:sqs:REGION:ACCOUNT_ID:queue-*"

Verification

After applying the fix, verify the policy explicitly restricts sqs:DeleteQueue to named queue ARNs rather than using wildcard "*" for the Resource parameter.

Priority

high - Estimated effort: 10 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
"aws_region": "us-west-2",
"user_pool_id": "${SharedUserPoolId}",
"client_id": "${SharedUserPoolClientId}",
"service_username": "crs-service-${TenantId}@rapticore.com",
"service_password": "{{resolve:secretsmanager:${CrsServiceUserPassword}:SecretString:password}}",
"target_account_id": "${AWS::AccountId}",
"api_endpoint": "https://${Domain}/api/v1/ingest/sarif",
"cognito_group": "${CognitoGroupName}",
"cross_account_role_arn": "arn:aws:iam::${CrsAccountId}:role/crs-cognito-access-standard-tenancy"
}
#####
# Queue Deletion Custom Resource
#####
QueueDeletionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"
QueueDeletionFunction:
Type: AWS::Lambda::Function
Type: AWS::SecretsManager::Secret
DependsOn: CreateCrsServiceUser
Properties:
Name: !Sub "CrsCredentials-${TenantId}"
SecretString: !Sub |
{
"tenant_id": "${TenantId}",
"aws_region": "us-west-2",
"user_pool_id": "${SharedUserPoolId}",
"client_id": "${SharedUserPoolClientId}",
"service_username": "crs-service-${TenantId}@rapticore.com",
"service_password": "{{resolve:secretsmanager:${CrsServiceUserPassword}:SecretString:password}}",
"target_account_id": "${AWS::AccountId}",
"api_endpoint": "https://${Domain}/api/v1/ingest/sarif",
"cognito_group": "${CognitoGroupName}",
"cross_account_role_arn": "arn:aws:iam::${CrsAccountId}:role/crs-cognito-access-standard-tenancy"
}
#####
# Queue Deletion Custom Resource
#####
QueueDeletionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: !GetAtt DeletionQueue.Arn
QueueDeletionFunction:
Type: AWS::Lambda::Function

🤖 Generated by Rapticore

Comment on lines +550 to +690
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"

QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
Code:
ZipFile: |
import boto3
import json
import cfnresponse
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
try:
logger.info(f"Event: {json.dumps(event)}")

sqs = boto3.client('sqs')
queue_arns = event['ResourceProperties'].get('QueueArns', [])

# Handle empty list or empty string
if not queue_arns or (len(queue_arns) == 1 and queue_arns[0] == ''):
logger.info("No queues to delete")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'Message': 'No queues specified',
'DeletedQueues': [],
'FailedQueues': [],
'TotalProcessed': 0,
'SuccessCount': 0,
'FailureCount': 0
})
return

deleted_queues = []
failed_queues = []

if event['RequestType'] in ['Create', 'Update']:
for queue_arn in queue_arns:
try:
# Extract queue URL from ARN
# ARN format: arn:aws:sqs:region:account:queue-name
arn_parts = queue_arn.split(':')
if len(arn_parts) >= 6:
region = arn_parts[3]
account = arn_parts[4]
queue_name = arn_parts[5]
queue_url = f"https://sqs.{region}.amazonaws.com/{account}/{queue_name}"
else:
logger.error(f"Invalid ARN format: {queue_arn}")
failed_queues.append(queue_arn)
continue

logger.info(f"Attempting to delete queue: {queue_url}")

# Check if queue exists first
try:
sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=['QueueArn'])
logger.info(f"Queue exists, proceeding with deletion: {queue_url}")
except sqs.exceptions.QueueDoesNotExist:
logger.info(f"Queue does not exist, skipping: {queue_url}")
deleted_queues.append(queue_arn)
continue

# Delete the queue
sqs.delete_queue(QueueUrl=queue_url)
deleted_queues.append(queue_arn)
logger.info(f"Successfully deleted queue: {queue_url}")

except Exception as e:
logger.error(f"Failed to delete queue {queue_arn}: {str(e)}")
failed_queues.append(queue_arn)

elif event['RequestType'] == 'Delete':
logger.info("Stack deletion - no queue cleanup needed")

# Prepare response
response_data = {
'DeletedQueues': deleted_queues,
'FailedQueues': failed_queues,
'TotalProcessed': len(queue_arns) if queue_arns and queue_arns[0] != '' else 0,
'SuccessCount': len(deleted_queues),
'FailureCount': len(failed_queues)
}

# Determine success/failure
if failed_queues:
logger.warning(f"Some queues failed to delete: {failed_queues}")
# Still report success if at least some queues were processed
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
else:
logger.info("All specified queues processed successfully")
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)

except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

DeleteSpecifiedQueues:
Type: Custom::QueueDeletion
Properties:
ServiceToken: !GetAtt QueueDeletionFunction.Arn
QueueArns: !Ref QueuesToDelete
Timestamp: "2025-08-13-20-30" # Change this to force re-execution

Outputs:
RapticoreStandardURL:
Value: !Join ["", ["https://", !Ref Domain]]
Description: Rapticore Standard URL

AzureWorkloadIdentityPoolId:
Description: The ID of the Cognito Identity Pool for Azure workload identity
Value: !Ref AzureWorkloadIdentityPool

CreatedIdentityId:
Description: The ID of the created identity in the identity pool
Value:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Lambda Function Missing DLQ Configuration

Severity: Low
Lines: 570-670

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0028)

Description

The AWS Lambda function is not configured with a Dead Letter Queue (DLQ). This increases the risk of unprocessed events, potentially leading to data loss or processing failures. Proper DLQ configuration ensures that failed events are captured for further investigation and reprocessing.

Remediation Summary

Fix Required

  • Add a Dead Letter Queue (DLQ) SQS resource to capture failed Lambda invocations
  • Configure the Lambda function's DeadLetterConfig property to reference the DLQ ARN (add after line 575, before the Code property)
  • Ensure the Lambda execution role has permissions to send messages to the DLQ (sqs:SendMessage)

Code Pattern

Add this after the Timeout: 300 line and before Code::

DeadLetterConfig:
  TargetArn: !GetAtt QueueDeletionDLQ.Arn

Also create the DLQ resource in the same template:

QueueDeletionDLQ:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: !Sub "QueueDeletion-DLQ-${TenantId}"

Add this permission to the QueueDeletionRole inline policy:

- Effect: Allow
  Action: sqs:SendMessage
  Resource: !GetAtt QueueDeletionDLQ.Arn

Verification

Confirm the Lambda function Properties block now includes DeadLetterConfig with a valid SQS queue ARN, and the execution role grants sqs:SendMessage permission to that queue.

Priority

medium - Estimated effort: 15 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"
QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
Code:
ZipFile: |
import boto3
import json
import cfnresponse
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
try:
logger.info(f"Event: {json.dumps(event)}")
sqs = boto3.client('sqs')
queue_arns = event['ResourceProperties'].get('QueueArns', [])
# Handle empty list or empty string
if not queue_arns or (len(queue_arns) == 1 and queue_arns[0] == ''):
logger.info("No queues to delete")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'Message': 'No queues specified',
'DeletedQueues': [],
'FailedQueues': [],
'TotalProcessed': 0,
'SuccessCount': 0,
'FailureCount': 0
})
return
deleted_queues = []
failed_queues = []
if event['RequestType'] in ['Create', 'Update']:
for queue_arn in queue_arns:
try:
# Extract queue URL from ARN
# ARN format: arn:aws:sqs:region:account:queue-name
arn_parts = queue_arn.split(':')
if len(arn_parts) >= 6:
region = arn_parts[3]
account = arn_parts[4]
queue_name = arn_parts[5]
queue_url = f"https://sqs.{region}.amazonaws.com/{account}/{queue_name}"
else:
logger.error(f"Invalid ARN format: {queue_arn}")
failed_queues.append(queue_arn)
continue
logger.info(f"Attempting to delete queue: {queue_url}")
# Check if queue exists first
try:
sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=['QueueArn'])
logger.info(f"Queue exists, proceeding with deletion: {queue_url}")
except sqs.exceptions.QueueDoesNotExist:
logger.info(f"Queue does not exist, skipping: {queue_url}")
deleted_queues.append(queue_arn)
continue
# Delete the queue
sqs.delete_queue(QueueUrl=queue_url)
deleted_queues.append(queue_arn)
logger.info(f"Successfully deleted queue: {queue_url}")
except Exception as e:
logger.error(f"Failed to delete queue {queue_arn}: {str(e)}")
failed_queues.append(queue_arn)
elif event['RequestType'] == 'Delete':
logger.info("Stack deletion - no queue cleanup needed")
# Prepare response
response_data = {
'DeletedQueues': deleted_queues,
'FailedQueues': failed_queues,
'TotalProcessed': len(queue_arns) if queue_arns and queue_arns[0] != '' else 0,
'SuccessCount': len(deleted_queues),
'FailureCount': len(failed_queues)
}
# Determine success/failure
if failed_queues:
logger.warning(f"Some queues failed to delete: {failed_queues}")
# Still report success if at least some queues were processed
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
else:
logger.info("All specified queues processed successfully")
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
DeleteSpecifiedQueues:
Type: Custom::QueueDeletion
Properties:
ServiceToken: !GetAtt QueueDeletionFunction.Arn
QueueArns: !Ref QueuesToDelete
Timestamp: "2025-08-13-20-30" # Change this to force re-execution
Outputs:
RapticoreStandardURL:
Value: !Join ["", ["https://", !Ref Domain]]
Description: Rapticore Standard URL
AzureWorkloadIdentityPoolId:
Description: The ID of the Cognito Identity Pool for Azure workload identity
Value: !Ref AzureWorkloadIdentityPool
CreatedIdentityId:
Description: The ID of the created identity in the identity pool
Value:
```yaml
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"
QueueDeletionDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub "QueueDeletion-DLQ-${TenantId}"
MessageRetentionPeriod: 1209600
QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
DeadLetterConfig:
TargetArn: !GetAtt QueueDeletionDLQ.Arn
Code:
ZipFile: |
import boto3
import json
import cfnresponse
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
try:
logger.info(f"Event: {json.dumps(event)}")
sqs = boto3.client('sqs')
queue_arns = event['ResourceProperties'].get('QueueArns', [])
# Handle empty list or empty string
if not queue_arns or (len(queue_arns) == 1 and queue_arns[0] == ''):
logger.info("No queues to delete")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'Message': 'No queues specified',
'DeletedQueues': [],
'FailedQueues': [],
'TotalProcessed': 0,
'SuccessCount': 0,
'FailureCount': 0
})
return
deleted_queues = []
failed_queues = []
if event['RequestType'] in ['Create', 'Update']:
for queue_arn in queue_arns:
try:
# Extract queue URL from ARN
# ARN format: arn:aws:sqs:region:account:queue-name
arn_parts = queue_arn.split(':')
if len(arn_parts) >= 6:
region = arn_parts[3]
account = arn_parts[4]
queue_name = arn_parts[5]
queue_url = f"https://sqs.{region}.amazonaws.com/{account}/{queue_name}"
else:
logger.error(f"Invalid ARN format: {queue_arn}")
failed_queues.append(queue_arn)
continue
logger.info(f"Attempting to delete queue: {queue_url}")
# Check if queue exists first
try:
sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=['QueueArn'])
logger.info(f"Queue exists, proceeding with deletion: {queue_url}")
except sqs.exceptions.QueueDoesNotExist:
logger.info(f"Queue does not exist, skipping: {queue_url}")
deleted_queues.append(queue_arn)
continue
# Delete the queue
sqs.delete_queue(QueueUrl=queue_url)
deleted_queues.append(queue_arn)
logger.info(f"Successfully deleted queue: {queue_url}")
except Exception as e:
logger.error(f"Failed to delete queue {queue_arn}: {str(e)}")
failed_queues.append(queue_arn)
elif event['RequestType'] == 'Delete':
logger.info("Stack deletion - no queue cleanup needed")
# Prepare response
response_data = {
'DeletedQueues': deleted_queues,
'FailedQueues': failed_queues,
'TotalProcessed': len(queue_arns) if queue_arns and queue_arns[0] != '' else 0,
'SuccessCount': len(deleted_queues),
'FailureCount': len(failed_queues)
}
# Determine success/failure
if failed_queues:
logger.warning(f"Some queues failed to delete: {failed_queues}")
# Still report success if at least some queues were processed
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
else:
logger.info("All specified queues processed successfully")
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
DeleteSpecifiedQueues:
Type: Custom::QueueDeletion
Properties:
ServiceToken: !GetAtt QueueDeletionFunction.Arn
QueueArns: !Ref QueuesToDelete
Timestamp: "2025-08-13-20-30" # Change this to force re-execution
Outputs:
RapticoreStandardURL:
Value: !Join ["", ["https://", !Ref Domain]]
Description: Rapticore Standard URL
AzureWorkloadIdentityPoolId:
Description: The ID of the Cognito Identity Pool for Azure workload identity
Value: !Ref AzureWorkloadIdentityPool
CreatedIdentityId:
Description: The ID of the created identity in the identity pool
Value:

---
🤖 Generated by Rapticore

Comment on lines +550 to +690
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"

QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
Code:
ZipFile: |
import boto3
import json
import cfnresponse
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
try:
logger.info(f"Event: {json.dumps(event)}")

sqs = boto3.client('sqs')
queue_arns = event['ResourceProperties'].get('QueueArns', [])

# Handle empty list or empty string
if not queue_arns or (len(queue_arns) == 1 and queue_arns[0] == ''):
logger.info("No queues to delete")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'Message': 'No queues specified',
'DeletedQueues': [],
'FailedQueues': [],
'TotalProcessed': 0,
'SuccessCount': 0,
'FailureCount': 0
})
return

deleted_queues = []
failed_queues = []

if event['RequestType'] in ['Create', 'Update']:
for queue_arn in queue_arns:
try:
# Extract queue URL from ARN
# ARN format: arn:aws:sqs:region:account:queue-name
arn_parts = queue_arn.split(':')
if len(arn_parts) >= 6:
region = arn_parts[3]
account = arn_parts[4]
queue_name = arn_parts[5]
queue_url = f"https://sqs.{region}.amazonaws.com/{account}/{queue_name}"
else:
logger.error(f"Invalid ARN format: {queue_arn}")
failed_queues.append(queue_arn)
continue

logger.info(f"Attempting to delete queue: {queue_url}")

# Check if queue exists first
try:
sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=['QueueArn'])
logger.info(f"Queue exists, proceeding with deletion: {queue_url}")
except sqs.exceptions.QueueDoesNotExist:
logger.info(f"Queue does not exist, skipping: {queue_url}")
deleted_queues.append(queue_arn)
continue

# Delete the queue
sqs.delete_queue(QueueUrl=queue_url)
deleted_queues.append(queue_arn)
logger.info(f"Successfully deleted queue: {queue_url}")

except Exception as e:
logger.error(f"Failed to delete queue {queue_arn}: {str(e)}")
failed_queues.append(queue_arn)

elif event['RequestType'] == 'Delete':
logger.info("Stack deletion - no queue cleanup needed")

# Prepare response
response_data = {
'DeletedQueues': deleted_queues,
'FailedQueues': failed_queues,
'TotalProcessed': len(queue_arns) if queue_arns and queue_arns[0] != '' else 0,
'SuccessCount': len(deleted_queues),
'FailureCount': len(failed_queues)
}

# Determine success/failure
if failed_queues:
logger.warning(f"Some queues failed to delete: {failed_queues}")
# Still report success if at least some queues were processed
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
else:
logger.info("All specified queues processed successfully")
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)

except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

DeleteSpecifiedQueues:
Type: Custom::QueueDeletion
Properties:
ServiceToken: !GetAtt QueueDeletionFunction.Arn
QueueArns: !Ref QueuesToDelete
Timestamp: "2025-08-13-20-30" # Change this to force re-execution

Outputs:
RapticoreStandardURL:
Value: !Join ["", ["https://", !Ref Domain]]
Description: Rapticore Standard URL

AzureWorkloadIdentityPoolId:
Description: The ID of the Cognito Identity Pool for Azure workload identity
Value: !Ref AzureWorkloadIdentityPool

CreatedIdentityId:
Description: The ID of the created identity in the identity pool
Value:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Lambda Function Not Configured in VPC

Severity: Medium
Lines: 570-670

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0026)

Description

The AWS Lambda function is not configured to run inside a VPC, which could expose it to unauthorized access. This configuration increases the risk of potential data breaches and attacks.

Remediation Summary

Fix Required

  • Add VPC configuration to the QueueDeletionFunction Lambda resource by including VpcConfig property with SecurityGroupIds and SubnetIds
  • Reference existing VPC, subnets, and security groups from your CloudFormation stack or create new ones if needed
  • Ensure the Lambda execution role has permissions for EC2 network interface operations (ec2:CreateNetworkInterface, ec2:DescribeNetworkInterfaces, ec2:DeleteNetworkInterface)

Code Pattern

Add to QueueDeletionFunction Properties section after Role property:
VpcConfig:
SecurityGroupIds:
- !Ref LambdaSecurityGroup
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2

Add inline policy to QueueDeletionRole with EC2 VPC permissions for network interface management.

Verification

Deploy the updated CloudFormation template and confirm the Lambda function shows VPC configuration in AWS Console under Configuration > VPC.

Priority

medium - Estimated effort: 15 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"
QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
Code:
ZipFile: |
import boto3
import json
import cfnresponse
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
try:
logger.info(f"Event: {json.dumps(event)}")
sqs = boto3.client('sqs')
queue_arns = event['ResourceProperties'].get('QueueArns', [])
# Handle empty list or empty string
if not queue_arns or (len(queue_arns) == 1 and queue_arns[0] == ''):
logger.info("No queues to delete")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'Message': 'No queues specified',
'DeletedQueues': [],
'FailedQueues': [],
'TotalProcessed': 0,
'SuccessCount': 0,
'FailureCount': 0
})
return
deleted_queues = []
failed_queues = []
if event['RequestType'] in ['Create', 'Update']:
for queue_arn in queue_arns:
try:
# Extract queue URL from ARN
# ARN format: arn:aws:sqs:region:account:queue-name
arn_parts = queue_arn.split(':')
if len(arn_parts) >= 6:
region = arn_parts[3]
account = arn_parts[4]
queue_name = arn_parts[5]
queue_url = f"https://sqs.{region}.amazonaws.com/{account}/{queue_name}"
else:
logger.error(f"Invalid ARN format: {queue_arn}")
failed_queues.append(queue_arn)
continue
logger.info(f"Attempting to delete queue: {queue_url}")
# Check if queue exists first
try:
sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=['QueueArn'])
logger.info(f"Queue exists, proceeding with deletion: {queue_url}")
except sqs.exceptions.QueueDoesNotExist:
logger.info(f"Queue does not exist, skipping: {queue_url}")
deleted_queues.append(queue_arn)
continue
# Delete the queue
sqs.delete_queue(QueueUrl=queue_url)
deleted_queues.append(queue_arn)
logger.info(f"Successfully deleted queue: {queue_url}")
except Exception as e:
logger.error(f"Failed to delete queue {queue_arn}: {str(e)}")
failed_queues.append(queue_arn)
elif event['RequestType'] == 'Delete':
logger.info("Stack deletion - no queue cleanup needed")
# Prepare response
response_data = {
'DeletedQueues': deleted_queues,
'FailedQueues': failed_queues,
'TotalProcessed': len(queue_arns) if queue_arns and queue_arns[0] != '' else 0,
'SuccessCount': len(deleted_queues),
'FailureCount': len(failed_queues)
}
# Determine success/failure
if failed_queues:
logger.warning(f"Some queues failed to delete: {failed_queues}")
# Still report success if at least some queues were processed
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
else:
logger.info("All specified queues processed successfully")
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})
DeleteSpecifiedQueues:
Type: Custom::QueueDeletion
Properties:
ServiceToken: !GetAtt QueueDeletionFunction.Arn
QueueArns: !Ref QueuesToDelete
Timestamp: "2025-08-13-20-30" # Change this to force re-execution
Outputs:
RapticoreStandardURL:
Value: !Join ["", ["https://", !Ref Domain]]
Description: Rapticore Standard URL
AzureWorkloadIdentityPoolId:
Description: The ID of the Cognito Identity Pool for Azure workload identity
Value: !Ref AzureWorkloadIdentityPool
CreatedIdentityId:
Description: The ID of the created identity in the identity pool
Value:
    Version: "2012-10-17"
    Statement:
      - Effect: Allow
        Principal:
          Service: lambda.amazonaws.com
        Action: sts:AssumeRole
  ManagedPolicyArns:
    - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
    - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
  Policies:
    - PolicyName: SQSDeletePolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - sqs:DeleteQueue
              - sqs:GetQueueAttributes
              - sqs:ListQueues
            Resource: "*"

QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
VpcConfig:
SecurityGroupIds:
- !Ref LambdaSecurityGroup
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
Code:
ZipFile: |
import boto3
import json
import cfnresponse
import logging

      logger = logging.getLogger()
      logger.setLevel(logging.INFO)

      def handler(event, context):
          try:
              logger.info(f"Event: {json.dumps(event)}")
              
              sqs = boto3.client('sqs')
              queue_arns = event['ResourceProperties'].get('QueueArns', [])
              
              # Handle empty list or empty string
              if not queue_arns or (len(queue_arns) == 1 and queue_arns[0] == ''):
                  logger.info("No queues to delete")
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, {
                      'Message': 'No queues specified',
                      'DeletedQueues': [],
                      'FailedQueues': [],
                      'TotalProcessed': 0,
                      'SuccessCount': 0,
                      'FailureCount': 0
                  })
                  return
              
              deleted_queues = []
              failed_queues = []
              
              if event['RequestType'] in ['Create', 'Update']:
                  for queue_arn in queue_arns:
                      try:
                          # Extract queue URL from ARN
                          # ARN format: arn:aws:sqs:region:account:queue-name
                          arn_parts = queue_arn.split(':')
                          if len(arn_parts) >= 6:
                              region = arn_parts[3]
                              account = arn_parts[4]
                              queue_name = arn_parts[5]
                              queue_url = f"https://sqs.{region}.amazonaws.com/{account}/{queue_name}"
                          else:
                              logger.error(f"Invalid ARN format: {queue_arn}")
                              failed_queues.append(queue_arn)
                              continue
                          
                          logger.info(f"Attempting to delete queue: {queue_url}")
                          
                          # Check if queue exists first
                          try:
                              sqs.get_queue_attributes(QueueUrl=queue_url, AttributeNames=['QueueArn'])
                              logger.info(f"Queue exists, proceeding with deletion: {queue_url}")
                          except sqs.exceptions.QueueDoesNotExist:
                              logger.info(f"Queue does not exist, skipping: {queue_url}")
                              deleted_queues.append(queue_arn)
                              continue
                          
                          # Delete the queue
                          sqs.delete_queue(QueueUrl=queue_url)
                          deleted_queues.append(queue_arn)
                          logger.info(f"Successfully deleted queue: {queue_url}")
                          
                      except Exception as e:
                          logger.error(f"Failed to delete queue {queue_arn}: {str(e)}")
                          failed_queues.append(queue_arn)
              
              elif event['RequestType'] == 'Delete':
                  logger.info("Stack deletion - no queue cleanup needed")
              
              # Prepare response
              response_data = {
                  'DeletedQueues': deleted_queues,
                  'FailedQueues': failed_queues,
                  'TotalProcessed': len(queue_arns) if queue_arns and queue_arns[0] != '' else 0,
                  'SuccessCount': len(deleted_queues),
                  'FailureCount': len(failed_queues)
              }
              
              # Determine success/failure
              if failed_queues:
                  logger.warning(f"Some queues failed to delete: {failed_queues}")
                  # Still report success if at least some queues were processed
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
              else:
                  logger.info("All specified queues processed successfully")
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
                  
          except Exception as e:
              logger.error(f"Unexpected error: {str(e)}")
              cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': str(e)})

DeleteSpecifiedQueues:
Type: Custom::QueueDeletion
Properties:
ServiceToken: !GetAtt QueueDeletionFunction.Arn
QueueArns: !Ref QueuesToDelete
Timestamp: "2025-08-13-20-30" # Change this to force re-execution

Outputs:
RapticoreStandardURL:
Value: !Join ["", ["https://", !Ref Domain]]
Description: Rapticore Standard URL

AzureWorkloadIdentityPoolId:
Description: The ID of the Cognito Identity Pool for Azure workload identity
Value: !Ref AzureWorkloadIdentityPool

CreatedIdentityId:
Description: The ID of the created identity in the identity pool
Value:


🤖 Generated by Rapticore

Comment on lines +550 to +580
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"

QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
Code:
ZipFile: |
import boto3

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security Finding: Lambda Function Missing Concurrency Limit

Severity: Low
Lines: 570-670

Actions

  • Mark as Ignored - Resolve this finding manually (Finding ID: RCOR-0031)

Description

AWS Lambda function lacks a function-level concurrent execution limit, which could lead to resource exhaustion under heavy load. This misconfiguration can result in performance degradation or service disruptions.

Remediation Summary

Fix Required

  • Add ReservedConcurrentExecutions property to the QueueDeletionFunction Lambda resource to limit concurrent executions and prevent resource exhaustion
  • Set this value based on your expected concurrency needs (typically 1-10 for custom resource functions that execute infrequently)
  • This property should be added at the same level as Timeout and Role in the Properties section

Code Pattern

Add the following line after the Timeout: 300 property:

ReservedConcurrentExecutions: 5

Adjust the numeric value (5) based on your actual concurrency requirements. For custom resource Lambda functions that handle queue deletion, a low value (1-10) is typically appropriate.

Verification

Deploy the updated CloudFormation template and verify the Lambda function's Concurrency settings in the AWS Console show a reserved concurrency limit instead of "Unreserved".

Priority

medium - Estimated effort: 2 minutes


Suggested Fix

Click "Commit suggestion" to apply this fix:

Suggested change
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"
QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
Code:
ZipFile: |
import boto3
Looking at the finding, the Lambda function `QueueDeletionFunction` is missing a concurrency limit configuration. This needs to be added as a property to protect against resource exhaustion.
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSDeletePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sqs:DeleteQueue
- sqs:GetQueueAttributes
- sqs:ListQueues
Resource: "*"
QueueDeletionFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "QueueDeletion-${TenantId}"
Runtime: python3.11
Handler: index.handler
Role: !GetAtt QueueDeletionRole.Arn
Timeout: 300
ReservedConcurrentExecutions: 10
Code:
ZipFile: |
import boto3

🤖 Generated by Rapticore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants