[Rapticore] Security Findings - hamza/RCOR-2704 (Part 1)#39
[Rapticore] Security Findings - hamza/RCOR-2704 (Part 1)#39rapticore-github[bot] wants to merge 94 commits into
Conversation
added policies related to tags
Co-authored-by: Azeem Irshad <azeem.irshad@rapticore.com>
Co-authored-by: Azeem Irshad <azeem.irshad@rapticore.com>
freemium_update
changed the name of the standard cfn to standardsomething
| 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: |
There was a problem hiding this comment.
🔒 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:
| 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
|
|
||
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
|
|
||
| # 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: |
There was a problem hiding this comment.
🔒 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:
| # 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
| # 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 |
There was a problem hiding this comment.
🔒 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:
| # 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
| 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: |
There was a problem hiding this comment.
🔒 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:
| 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
| 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: |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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
KmsMasterKeyIdproperty to the SQS Queue resource (lines 237-244) to enable server-side encryption - Use AWS managed key
alias/aws/sqsor 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/sqsFor customer-managed keys, use:
KmsMasterKeyId: !GetAtt MyKmsKey.ArnVerification
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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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
KmsMasterKeyIdproperty 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/sqsfor AWS-managed encryption - Apply this change to the
RealTimeThreatAlertTriggerQueueresource 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
|
|
||
| # 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: |
There was a problem hiding this comment.
🔒 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:
| # 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
| 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 | ||
| ##### |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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.ArnAlternatively, use AWS managed key:
KmsMasterKeyId: alias/aws/sqsVerification
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:
| 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
| 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: |
There was a problem hiding this comment.
🔒 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/sqsFor customer-managed keys, use the full ARN:
KmsMasterKeyId: !GetAtt SQSEncryptionKey.ArnVerification
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:
| 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
| # 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: |
There was a problem hiding this comment.
🔒 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:
| # 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
| - 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}", |
There was a problem hiding this comment.
🔒 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: 1209600Add to Lambda Properties (after Timeout property):
DeadLetterConfig:
TargetArn: !GetAtt CrsUserManagementDLQ.ArnEnsure 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:
| - 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
| - 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}", |
There was a problem hiding this comment.
🔒 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
ReservedConcurrentExecutionsproperty 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, andTimeout
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:
| - 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
| - 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}", |
There was a problem hiding this comment.
🔒 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:
| - 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
|
|
||
| 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: |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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:
| 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
| 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: |
There was a problem hiding this comment.
🔒 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
DeadLetterConfigproperty to reference the DLQ ARN (add after line 575, before theCodeproperty) - 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:
| 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
| 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: |
There was a problem hiding this comment.
🔒 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:
| 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
| 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 |
There was a problem hiding this comment.
🔒 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
ReservedConcurrentExecutionsproperty to theQueueDeletionFunctionLambda 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
TimeoutandRolein 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:
| 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
Rapticore Security Findings (Part 1)
This is a draft PR containing automated security fix recommendations from Rapticore.
How to use this PR:
🤖 Generated by Rapticore