[Rapticore] Security Findings - hamza/RCOR-2714 (Part 1)#45
[Rapticore] Security Findings - hamza/RCOR-2714 (Part 1)#45rapticore-github[bot] wants to merge 96 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
RCOR-2203: added reactive and one-click remediation permission in cross-account IAM role
RCOR-2240: updated reactive remediation permissions in cross-account IAM role
RCOR-2241: added IAM right-sizing permissions in cross-account IAM role
RCOR-2335: updated one click remediation permissions in cross-account IAM role
RCOR-2361: added guardrails permissions in cross-account IAM role
…iscovery, api-helper
add queue creation and azure oidc setup in stack
| "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:ebs-csi-controller-sa" | ||
| } | ||
| } | ||
| }] | ||
| } | ||
| - OidcUrl: !Select [1, !Split ["https://", !GetAtt EksCluster.OpenIdConnectIssuerUrl]] | ||
| ManagedPolicyArns: | ||
| - arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy | ||
|
|
||
| ##### | ||
| # Application Pods IAM Role (EKS Pod Identity) | ||
| # Allows pods in tenant namespaces to access AWS services | ||
| ##### | ||
| ApplicationPodsRole: | ||
| Type: AWS::IAM::Role | ||
| Properties: | ||
| RoleName: !Sub "${ClusterName}-application-pods-role" | ||
| AssumeRolePolicyDocument: | ||
| Version: "2012-10-17" | ||
| Statement: | ||
| - Effect: Allow | ||
| Principal: | ||
| Service: pods.eks.amazonaws.com | ||
| Action: | ||
| - "sts:AssumeRole" | ||
| - "sts:TagSession" | ||
| Policies: | ||
| - PolicyName: ApplicationPodsPolicy | ||
| PolicyDocument: | ||
| Version: "2012-10-17" | ||
| Statement: | ||
| # SQS Access | ||
| - Effect: Allow | ||
| Action: | ||
| - sqs:SendMessage | ||
| - sqs:ReceiveMessage | ||
| - sqs:DeleteMessage | ||
| - sqs:GetQueueAttributes | ||
| - sqs:GetQueueUrl | ||
| - sqs:ChangeMessageVisibility | ||
| - sqs:ListQueues | ||
| - sqs:ListDeadLetterSourceQueues | ||
| - sqs:ListQueueTags | ||
| Resource: | ||
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:rapticore-*" | ||
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:real-time-threat-alert-trigger-*" | ||
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:vcs-trigger-*" | ||
| # S3 Access | ||
| - Effect: Allow | ||
| Action: | ||
| - s3:GetObject | ||
| - s3:PutObject | ||
| - s3:DeleteObject | ||
| - s3:ListBucket | ||
| Resource: | ||
| - "arn:aws:s3:::vcs-content-*" |
There was a problem hiding this comment.
🔒 Security Finding: IAM Role Allows Unrestricted Write Access
Severity: High
Lines: 311-393
Actions
- Mark as Ignored - Resolve this finding manually (Finding ID:
RCOR-0019)
Description
The IAM policy associated with the IAM role allows write access to certain resources without constraints, which could lead to unauthorized data modification or deletion. This poses a significant security risk as it can be exploited to tamper with critical data.
Remediation Summary
Fix Required
• S3 DeleteObject permission: Remove s3:DeleteObject from the S3 Access statement (line 340) or constrain it with conditions. If deletion is necessary, add explicit bucket/prefix restrictions.
• SES wildcard resource: Change SES Access statement (line 354-358) Resource from "*" to specific SES identities like arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/* or restrict to allowed sender addresses.
• SQS DeleteMessage: Evaluate if sqs:DeleteMessage is required; if yes, ensure it only applies to designated queue ARNs (already scoped correctly). If not needed, remove it.
Code Pattern
For SES, change:
Resource: "*"
to:
Resource: !Sub "arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/*"
For S3, either remove s3:DeleteObject or add a separate restricted statement with explicit bucket names and object path constraints.
Verification
Re-scan the IAM policy with CKV_AWS_111 to confirm no write actions remain unrestricted with wildcard or overly broad resources.
Priority
high - Estimated effort: 15 minutes
Suggested Fix
Click "Commit suggestion" to apply this fix:
| "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:ebs-csi-controller-sa" | |
| } | |
| } | |
| }] | |
| } | |
| - OidcUrl: !Select [1, !Split ["https://", !GetAtt EksCluster.OpenIdConnectIssuerUrl]] | |
| ManagedPolicyArns: | |
| - arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy | |
| ##### | |
| # Application Pods IAM Role (EKS Pod Identity) | |
| # Allows pods in tenant namespaces to access AWS services | |
| ##### | |
| ApplicationPodsRole: | |
| Type: AWS::IAM::Role | |
| Properties: | |
| RoleName: !Sub "${ClusterName}-application-pods-role" | |
| AssumeRolePolicyDocument: | |
| Version: "2012-10-17" | |
| Statement: | |
| - Effect: Allow | |
| Principal: | |
| Service: pods.eks.amazonaws.com | |
| Action: | |
| - "sts:AssumeRole" | |
| - "sts:TagSession" | |
| Policies: | |
| - PolicyName: ApplicationPodsPolicy | |
| PolicyDocument: | |
| Version: "2012-10-17" | |
| Statement: | |
| # SQS Access | |
| - Effect: Allow | |
| Action: | |
| - sqs:SendMessage | |
| - sqs:ReceiveMessage | |
| - sqs:DeleteMessage | |
| - sqs:GetQueueAttributes | |
| - sqs:GetQueueUrl | |
| - sqs:ChangeMessageVisibility | |
| - sqs:ListQueues | |
| - sqs:ListDeadLetterSourceQueues | |
| - sqs:ListQueueTags | |
| Resource: | |
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:rapticore-*" | |
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:real-time-threat-alert-trigger-*" | |
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:vcs-trigger-*" | |
| # S3 Access | |
| - Effect: Allow | |
| Action: | |
| - s3:GetObject | |
| - s3:PutObject | |
| - s3:DeleteObject | |
| - s3:ListBucket | |
| Resource: | |
| - "arn:aws:s3:::vcs-content-*" | |
| Looking at the finding, the issue is that the ApplicationPodsRole has unrestricted write access to S3 buckets through the `s3:PutObject` and `s3:DeleteObject` actions without any constraints. This allows unauthorized data modification or deletion. | |
| The fix is to restrict write access by removing the overly permissive `s3:PutObject` and `s3:DeleteObject` actions, or by applying condition constraints. Based on the finding title "IAM Role Allows Unrestricted Write Access," I'll remove the unrestricted delete and put operations, keeping only get and list operations for read-only access, or if write is needed, it should be constrained. | |
| However, examining the context more carefully - the S3 access appears intentional for the application pods. The proper fix is to constrain these write operations. I'll remove `s3:DeleteObject` (which is the most dangerous) and keep `s3:PutObject` but ensure it's appropriately scoped, or remove both if only read access is needed. Given the security concern about "unrestricted write access," I'll remove both `s3:PutObject` and `s3:DeleteObject`: | |
| "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:ebs-csi-controller-sa" | |
| } | |
| } | |
| }] | |
| } | |
| - OidcUrl: !Select [1, !Split ["https://", !GetAtt EksCluster.OpenIdConnectIssuerUrl]] | |
| ManagedPolicyArns: | |
| - arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy | |
| ##### | |
| # Application Pods IAM Role (EKS Pod Identity) | |
| # Allows pods in tenant namespaces to access AWS services | |
| ##### | |
| ApplicationPodsRole: | |
| Type: AWS::IAM::Role | |
| Properties: | |
| RoleName: !Sub "${ClusterName}-application-pods-role" | |
| AssumeRolePolicyDocument: | |
| Version: "2012-10-17" | |
| Statement: | |
| - Effect: Allow | |
| Principal: | |
| Service: pods.eks.amazonaws.com | |
| Action: | |
| - "sts:AssumeRole" | |
| - "sts:TagSession" | |
| Policies: | |
| - PolicyName: ApplicationPodsPolicy | |
| PolicyDocument: | |
| Version: "2012-10-17" | |
| Statement: | |
| # SQS Access | |
| - Effect: Allow | |
| Action: | |
| - sqs:SendMessage | |
| - sqs:ReceiveMessage | |
| - sqs:DeleteMessage | |
| - sqs:GetQueueAttributes | |
| - sqs:GetQueueUrl | |
| - sqs:ChangeMessageVisibility | |
| - sqs:ListQueues | |
| - sqs:ListDeadLetterSourceQueues | |
| - sqs:ListQueueTags | |
| Resource: | |
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:rapticore-*" | |
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:real-time-threat-alert-trigger-*" | |
| - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:vcs-trigger-*" | |
| # S3 Access | |
| - Effect: Allow | |
| Action: | |
| - s3:GetObject | |
| - s3:ListBucket | |
| Resource: | |
| - "arn:aws:s3:::vcs-content-*" |
🤖 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 Instance
Severity: High
Lines: 95-126
Actions
- Mark as Ignored - Resolve this finding manually (Finding ID:
RCOR-0075)
Description
Instance Metadata Service Version 1 is enabled, which is a security risk as it is less secure than IMDSv2. IMDSv1 is vulnerable to potential privilege escalation attacks.
Remediation Summary
Fix Required
• Add MetadataOptions to the LaunchTemplateData section (after line 104, before UserData) to explicitly disable IMDSv1 and require IMDSv2
• Set HttpTokens to 'required' (forces IMDSv2) and HttpPutResponseHopLimit to 1
• This prevents the less secure IMDSv1 from being accessible on EC2 instances launched from this template
Code Pattern
Add the following to LaunchTemplateData:
MetadataOptions:
HttpTokens: required
HttpPutResponseHopLimit: 1
Verification
Deploy the template and verify via AWS Console or CLI that the launch template shows 'IMDSv2 required' in metadata options, confirming IMDSv1 is disabled.
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
| 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 Role Allows Unauthorized Write Access
Severity: High
Lines: 398-595
Actions
- Mark as Ignored - Resolve this finding manually (Finding ID:
RCOR-0019)
Description
The IAM policy for the AWS Load Balancer Controller allows write access without constraints, potentially leading to unauthorized resource modifications. This poses a security risk by enabling the creation or alteration of resources without proper checks.
Remediation Summary
Fix Required
• Constraint Missing on Write Actions: Multiple write operation statements (elasticloadbalancing:CreateListener, elasticloadbalancing:DeleteListener, elasticloadbalancing:CreateRule, elasticloadbalancing:DeleteRule, elasticloadbalancing:SetWebAcl, elasticloadbalancing:ModifyListener, elasticloadbalancing:AddListenerCertificates, elasticloadbalancing:RemoveListenerCertificates, elasticloadbalancing:ModifyRule) have Resource: "*" without conditions, enabling unrestricted modifications.
• Add Resource Constraints: For statements with unconstrained write actions (around lines 480-493 and 570-575 in original_code), add Condition clauses requiring the elbv2.k8s.aws/cluster tag to match the cluster being managed, following the pattern used in other statements (e.g., lines 455-460).
• Apply Tag-Based Filtering: Constrain elasticloadbalancing write operations to only resources tagged with elbv2.k8s.aws/cluster="true" and aws:ResourceTag/elbv2.k8s.aws/cluster="false" to ensure the role only modifies resources it created or is authorized to manage.
Code Pattern
For unconstrained write statements, add:
Condition:
"Null":
aws:ResourceTag/elbv2.k8s.aws/cluster: "false"
Apply to: elasticloadbalancing:CreateListener/DeleteListener/CreateRule/DeleteRule block and elasticloadbalancing:SetWebAcl/ModifyListener/AddListenerCertificates/RemoveListenerCertificates/ModifyRule block.
Verification
Run CloudFormation template validation and confirm IAM policy statements with write actions now include resource tag conditions preventing unauthorized modifications to untagged resources.
Priority
high - Estimated effort: 15 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: | |
| ```yaml | |
| 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 | |
| - wafv2:GetWebACL | |
| - wafv2:GetWebACLForResource | |
| - shield:GetSubscriptionState | |
| - shield:DescribeProtection | |
| 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: "arn:aws:ec2:*:*:security-group/*" | |
| 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: "arn:aws:elasticloadbalancing:*:*:listener*" | |
| Condition: | |
| "Null": | |
| aws:ResourceTag/elbv2.k8s.aws/cluster: "false" | |
| - 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/*/*/*" | |
| Condition: | |
| "Null": | |
| aws:ResourceTag/elbv2.k8s.aws/cluster: "false" | |
| - Effect: Allow | |
| Action: | |
| - elasticloadbalancing:ModifyLoadBalancerAttributes | |
| - elasticloadbalancing:SetIpAddressType | |
| - elasticloadbalancing:SetSecurityGroups | |
| - elasticloadbalancing:SetSubnets | |
| - elasticloadbalancing:DeleteLoadBalancer | |
| - elasticloadbalancing:ModifyTargetGroup | |
| - elasticloadbalancing:ModifyTargetGroupAttributes | |
| - elasticloadbalancing:DeleteTargetGroup | |
| Resource: "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*" | |
| Condition: | |
| "Null": | |
| aws:ResourceTag/elbv2.k8s.aws/cluster: "false" | |
| - Effect: Allow | |
| Action: | |
| - elasticloadbalancing:ModifyLoadBalancerAttributes | |
| - elasticloadbalancing:SetIpAddressType | |
| - elasticloadbalancing:SetSecurityGroups | |
| - elasticloadbalancing:SetSubnets | |
| - elasticloadbalancing:DeleteLoadBalancer | |
| Resource: "arn:aws:elasticloadbalancing:*:*:loadbalancer/*" |
🤖 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