From 5585f8a835f07fefafff4de0b7d2158aa053a707 Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 16 Oct 2024 11:25:30 +0000 Subject: [PATCH 01/18] feat: add collection of guardduty resource --- .../aws/fix_plugin_aws/resource/bedrock.py | 18 +- .../aws/fix_plugin_aws/resource/guardduty.py | 1384 +++++++++++++++++ plugins/aws/tools/aws_model_gen.py | 8 + 3 files changed, 1401 insertions(+), 9 deletions(-) create mode 100644 plugins/aws/fix_plugin_aws/resource/guardduty.py diff --git a/plugins/aws/fix_plugin_aws/resource/bedrock.py b/plugins/aws/fix_plugin_aws/resource/bedrock.py index e2003749f2..d4aecec800 100644 --- a/plugins/aws/fix_plugin_aws/resource/bedrock.py +++ b/plugins/aws/fix_plugin_aws/resource/bedrock.py @@ -221,7 +221,7 @@ def add_tags(job: AwsResource) -> None: modelIdentifier=js["modelArn"], ): if instance := cls.from_api(result, builder): - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work(service_name, add_tags, instance) @@ -505,7 +505,7 @@ def add_tags(job: AwsResource) -> None: guardrailVersion=js["version"], ): if instance := cls.from_api(result, builder): - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work(service_name, add_tags, instance) @@ -637,7 +637,7 @@ def add_tags(job: AwsResource) -> None: jobIdentifier=js["jobArn"], ): if instance := cls.from_api(result, builder): - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work(service_name, add_tags, instance) @@ -838,7 +838,7 @@ def add_tags(job: AwsResource) -> None: jobIdentifier=js["jobArn"], ): if instance := cls.from_api(result, builder): - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work(service_name, add_tags, instance) @@ -1037,7 +1037,7 @@ def add_tags(agent: AwsResource) -> None: ): if instance := AwsBedrockAgent.from_api(result, builder): instance.agent_version = js["latestAgentVersion"] - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work("bedrock-agent", add_tags, instance) @@ -1324,7 +1324,7 @@ def add_tags(knowledge_base: AwsResource) -> None: knowledgeBaseId=js["knowledgeBaseId"], ): if instance := cls.from_api(result, builder): - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work(service_name, add_tags, instance) @classmethod @@ -1492,7 +1492,7 @@ def add_tags(prompt: AwsResource) -> None: promptVersion=js["version"], ): if instance := cls.from_api(result, builder): - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work("bedrock-agent", add_tags, instance) @@ -1822,7 +1822,7 @@ def collect_flow_versions(flow: AwsBedrockAgentFlow) -> None: flowVersion=flow.version, ): if instance := AwsBedrockAgentFlowVersion.from_api(result, builder): - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work("bedrock-agent", add_tags, instance) for js in json: @@ -1834,7 +1834,7 @@ def collect_flow_versions(flow: AwsBedrockAgentFlow) -> None: if instance := AwsBedrockAgentFlow.from_api(result, builder): if not instance.version: instance.version = js["version"] - builder.add_node(instance, js) + builder.add_node(instance, result) builder.submit_work("bedrock-agent", add_tags, instance) builder.submit_work("bedrock-agent", collect_flow_versions, instance) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py new file mode 100644 index 0000000000..0d78064ab8 --- /dev/null +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -0,0 +1,1384 @@ +from datetime import datetime +from typing import ClassVar, Dict, List, Optional, Tuple, Type, Any +from attrs import define, field + +from fix_plugin_aws.aws_client import AwsClient +from fix_plugin_aws.resource.base import AwsApiSpec, AwsResource, GraphBuilder +from fix_plugin_aws.utils import ToDict +from fixlib.baseresources import ModelReference, PhantomBaseResource +from fixlib.graph import Graph +from fixlib.json_bender import S, Bend, Bender, ForallBend, bend +from fixlib.types import Json + + +service_name = "guardduty" + + +@define(eq=False, slots=False) +class AwsGuardDutyAccessKeyDetails: + kind: ClassVar[str] = "aws_guard_duty_access_key_details" + mapping: ClassVar[Dict[str, Bender]] = { + "access_key_id": S("AccessKeyId"), + "principal_id": S("PrincipalId"), + "user_name": S("UserName"), + "user_type": S("UserType"), + } + access_key_id: Optional[str] = field(default=None, metadata={"description": "The access key ID of the user."}) # fmt: skip + principal_id: Optional[str] = field(default=None, metadata={"description": "The principal ID of the user."}) # fmt: skip + user_name: Optional[str] = field(default=None, metadata={"description": "The name of the user."}) # fmt: skip + user_type: Optional[str] = field(default=None, metadata={"description": "The type of the user."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyDefaultServerSideEncryption: + kind: ClassVar[str] = "aws_guard_duty_default_server_side_encryption" + mapping: ClassVar[Dict[str, Bender]] = { + "encryption_type": S("EncryptionType"), + "kms_master_key_arn": S("KmsMasterKeyArn"), + } + encryption_type: Optional[str] = field(default=None, metadata={"description": "The type of encryption used for objects within the S3 bucket."}) # fmt: skip + kms_master_key_arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) of the KMS encryption key. Only available if the bucket EncryptionType is aws:kms."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyAccessControlList: + kind: ClassVar[str] = "aws_guard_duty_access_control_list" + mapping: ClassVar[Dict[str, Bender]] = { + "allows_public_read_access": S("AllowsPublicReadAccess"), + "allows_public_write_access": S("AllowsPublicWriteAccess"), + } + allows_public_read_access: Optional[bool] = field(default=None, metadata={"description": "A value that indicates whether public read access for the bucket is enabled through an Access Control List (ACL)."}) # fmt: skip + allows_public_write_access: Optional[bool] = field(default=None, metadata={"description": "A value that indicates whether public write access for the bucket is enabled through an Access Control List (ACL)."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyBucketPolicy: + kind: ClassVar[str] = "aws_guard_duty_bucket_policy" + mapping: ClassVar[Dict[str, Bender]] = { + "allows_public_read_access": S("AllowsPublicReadAccess"), + "allows_public_write_access": S("AllowsPublicWriteAccess"), + } + allows_public_read_access: Optional[bool] = field(default=None, metadata={"description": "A value that indicates whether public read access for the bucket is enabled through a bucket policy."}) # fmt: skip + allows_public_write_access: Optional[bool] = field(default=None, metadata={"description": "A value that indicates whether public write access for the bucket is enabled through a bucket policy."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyBlockPublicAccess: + kind: ClassVar[str] = "aws_guard_duty_block_public_access" + mapping: ClassVar[Dict[str, Bender]] = { + "ignore_public_acls": S("IgnorePublicAcls"), + "restrict_public_buckets": S("RestrictPublicBuckets"), + "block_public_acls": S("BlockPublicAcls"), + "block_public_policy": S("BlockPublicPolicy"), + } + ignore_public_acls: Optional[bool] = field(default=None, metadata={"description": "Indicates if S3 Block Public Access is set to IgnorePublicAcls."}) # fmt: skip + restrict_public_buckets: Optional[bool] = field(default=None, metadata={"description": "Indicates if S3 Block Public Access is set to RestrictPublicBuckets."}) # fmt: skip + block_public_acls: Optional[bool] = field(default=None, metadata={"description": "Indicates if S3 Block Public Access is set to BlockPublicAcls."}) # fmt: skip + block_public_policy: Optional[bool] = field(default=None, metadata={"description": "Indicates if S3 Block Public Access is set to BlockPublicPolicy."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyBucketLevelPermissions: + kind: ClassVar[str] = "aws_guard_duty_bucket_level_permissions" + mapping: ClassVar[Dict[str, Bender]] = { + "access_control_list": S("AccessControlList") >> Bend(AwsGuardDutyAccessControlList.mapping), + "bucket_policy": S("BucketPolicy") >> Bend(AwsGuardDutyBucketPolicy.mapping), + "block_public_access": S("BlockPublicAccess") >> Bend(AwsGuardDutyBlockPublicAccess.mapping), + } + access_control_list: Optional[AwsGuardDutyAccessControlList] = field(default=None, metadata={"description": "Contains information on how Access Control Policies are applied to the bucket."}) # fmt: skip + bucket_policy: Optional[AwsGuardDutyBucketPolicy] = field(default=None, metadata={"description": "Contains information on the bucket policies for the S3 bucket."}) # fmt: skip + block_public_access: Optional[AwsGuardDutyBlockPublicAccess] = field(default=None, metadata={"description": "Contains information on which account level S3 Block Public Access settings are applied to the S3 bucket."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyAccountLevelPermissions: + kind: ClassVar[str] = "aws_guard_duty_account_level_permissions" + mapping: ClassVar[Dict[str, Bender]] = { + "block_public_access": S("BlockPublicAccess") >> Bend(AwsGuardDutyBlockPublicAccess.mapping) + } + block_public_access: Optional[AwsGuardDutyBlockPublicAccess] = field(default=None, metadata={"description": "Describes the S3 Block Public Access settings of the bucket's parent account."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyPermissionConfiguration: + kind: ClassVar[str] = "aws_guard_duty_permission_configuration" + mapping: ClassVar[Dict[str, Bender]] = { + "bucket_level_permissions": S("BucketLevelPermissions") >> Bend(AwsGuardDutyBucketLevelPermissions.mapping), + "account_level_permissions": S("AccountLevelPermissions") >> Bend(AwsGuardDutyAccountLevelPermissions.mapping), + } + bucket_level_permissions: Optional[AwsGuardDutyBucketLevelPermissions] = field(default=None, metadata={"description": "Contains information about the bucket level permissions for the S3 bucket."}) # fmt: skip + account_level_permissions: Optional[AwsGuardDutyAccountLevelPermissions] = field(default=None, metadata={"description": "Contains information about the account level permissions on the S3 bucket."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyPublicAccess: + kind: ClassVar[str] = "aws_guard_duty_public_access" + mapping: ClassVar[Dict[str, Bender]] = { + "permission_configuration": S("PermissionConfiguration") >> Bend(AwsGuardDutyPermissionConfiguration.mapping), + "effective_permission": S("EffectivePermission"), + } + permission_configuration: Optional[AwsGuardDutyPermissionConfiguration] = field(default=None, metadata={"description": "Contains information about how permissions are configured for the S3 bucket."}) # fmt: skip + effective_permission: Optional[str] = field(default=None, metadata={"description": "Describes the effective permission on this bucket after factoring all attached policies."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyS3ObjectDetail: + kind: ClassVar[str] = "aws_guard_duty_s3_object_detail" + mapping: ClassVar[Dict[str, Bender]] = { + "object_arn": S("ObjectArn"), + "key": S("Key"), + "e_tag": S("ETag"), + "hash": S("Hash"), + "version_id": S("VersionId"), + } + object_arn: Optional[str] = field(default=None, metadata={"description": "Amazon Resource Name (ARN) of the S3 object."}) # fmt: skip + key: Optional[str] = field(default=None, metadata={"description": "Key of the S3 object."}) # fmt: skip + e_tag: Optional[str] = field(default=None, metadata={"description": "The entity tag is a hash of the S3 object. The ETag reflects changes only to the contents of an object, and not its metadata."}) # fmt: skip + hash: Optional[str] = field(default=None, metadata={"description": "Hash of the threat detected in this finding."}) # fmt: skip + version_id: Optional[str] = field(default=None, metadata={"description": "Version ID of the object."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyS3BucketDetail: + kind: ClassVar[str] = "aws_guard_duty_s3_bucket_detail" + mapping: ClassVar[Dict[str, Bender]] = { + "arn": S("Arn"), + "name": S("Name"), + "type": S("Type"), + "created_at": S("CreatedAt"), + "owner": S("Owner", "Id"), + "default_server_side_encryption": S("DefaultServerSideEncryption") + >> Bend(AwsGuardDutyDefaultServerSideEncryption.mapping), + "public_access": S("PublicAccess") >> Bend(AwsGuardDutyPublicAccess.mapping), + "s3_object_details": S("S3ObjectDetails", default=[]) >> ForallBend(AwsGuardDutyS3ObjectDetail.mapping), + } + arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) of the S3 bucket."}) # fmt: skip + name: Optional[str] = field(default=None, metadata={"description": "The name of the S3 bucket."}) # fmt: skip + type: Optional[str] = field(default=None, metadata={"description": "Describes whether the bucket is a source or destination bucket."}) # fmt: skip + created_at: Optional[datetime] = field(default=None, metadata={"description": "The date and time the bucket was created at."}) # fmt: skip + owner: Optional[str] = field(default=None, metadata={"description": "The owner of the S3 bucket."}) # fmt: skip + default_server_side_encryption: Optional[AwsGuardDutyDefaultServerSideEncryption] = field(default=None, metadata={"description": "Describes the server side encryption method used in the S3 bucket."}) # fmt: skip + public_access: Optional[AwsGuardDutyPublicAccess] = field(default=None, metadata={"description": "Describes the public access policies that apply to the S3 bucket."}) # fmt: skip + s3_object_details: Optional[List[AwsGuardDutyS3ObjectDetail]] = field(factory=list, metadata={"description": "Information about the S3 object that was scanned."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyIamInstanceProfile: + kind: ClassVar[str] = "aws_guard_duty_iam_instance_profile" + mapping: ClassVar[Dict[str, Bender]] = {"arn": S("Arn"), "id": S("Id")} + arn: Optional[str] = field(default=None, metadata={"description": "The profile ARN of the EC2 instance."}) # fmt: skip + id: Optional[str] = field(default=None, metadata={"description": "The profile ID of the EC2 instance."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyPrivateIpAddressDetails: + kind: ClassVar[str] = "aws_guard_duty_private_ip_address_details" + mapping: ClassVar[Dict[str, Bender]] = { + "private_dns_name": S("PrivateDnsName"), + "private_ip_address": S("PrivateIpAddress"), + } + private_dns_name: Optional[str] = field(default=None, metadata={"description": "The private DNS name of the EC2 instance."}) # fmt: skip + private_ip_address: Optional[str] = field(default=None, metadata={"description": "The private IP address of the EC2 instance."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutySecurityGroup: + kind: ClassVar[str] = "aws_guard_duty_security_group" + mapping: ClassVar[Dict[str, Bender]] = {"group_id": S("GroupId"), "group_name": S("GroupName")} + group_id: Optional[str] = field(default=None, metadata={"description": "The security group ID of the EC2 instance."}) # fmt: skip + group_name: Optional[str] = field(default=None, metadata={"description": "The security group name of the EC2 instance."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyNetworkInterface: + kind: ClassVar[str] = "aws_guard_duty_network_interface" + mapping: ClassVar[Dict[str, Bender]] = { + "ipv6_addresses": S("Ipv6Addresses", default=[]), + "network_interface_id": S("NetworkInterfaceId"), + "private_dns_name": S("PrivateDnsName"), + "private_ip_address": S("PrivateIpAddress"), + "private_ip_addresses": S("PrivateIpAddresses", default=[]) + >> ForallBend(AwsGuardDutyPrivateIpAddressDetails.mapping), + "public_dns_name": S("PublicDnsName"), + "public_ip": S("PublicIp"), + "security_groups": S("SecurityGroups", default=[]) >> ForallBend(AwsGuardDutySecurityGroup.mapping), + "subnet_id": S("SubnetId"), + "vpc_id": S("VpcId"), + } + ipv6_addresses: Optional[List[str]] = field(factory=list, metadata={"description": "A list of IPv6 addresses for the EC2 instance."}) # fmt: skip + network_interface_id: Optional[str] = field(default=None, metadata={"description": "The ID of the network interface."}) # fmt: skip + private_dns_name: Optional[str] = field(default=None, metadata={"description": "The private DNS name of the EC2 instance."}) # fmt: skip + private_ip_address: Optional[str] = field(default=None, metadata={"description": "The private IP address of the EC2 instance."}) # fmt: skip + private_ip_addresses: Optional[List[AwsGuardDutyPrivateIpAddressDetails]] = field(factory=list, metadata={"description": "Other private IP address information of the EC2 instance."}) # fmt: skip + public_dns_name: Optional[str] = field(default=None, metadata={"description": "The public DNS name of the EC2 instance."}) # fmt: skip + public_ip: Optional[str] = field(default=None, metadata={"description": "The public IP address of the EC2 instance."}) # fmt: skip + security_groups: Optional[List[AwsGuardDutySecurityGroup]] = field(factory=list, metadata={"description": "The security groups associated with the EC2 instance."}) # fmt: skip + subnet_id: Optional[str] = field(default=None, metadata={"description": "The subnet ID of the EC2 instance."}) # fmt: skip + vpc_id: Optional[str] = field(default=None, metadata={"description": "The VPC ID of the EC2 instance."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyProductCode: + kind: ClassVar[str] = "aws_guard_duty_product_code" + mapping: ClassVar[Dict[str, Bender]] = {"code": S("Code"), "product_type": S("ProductType")} + code: Optional[str] = field(default=None, metadata={"description": "The product code information."}) # fmt: skip + product_type: Optional[str] = field(default=None, metadata={"description": "The product code type."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyInstanceDetails: + kind: ClassVar[str] = "aws_guard_duty_instance_details" + mapping: ClassVar[Dict[str, Bender]] = { + "availability_zone": S("AvailabilityZone"), + "iam_instance_profile": S("IamInstanceProfile") >> Bend(AwsGuardDutyIamInstanceProfile.mapping), + "image_description": S("ImageDescription"), + "image_id": S("ImageId"), + "instance_id": S("InstanceId"), + "instance_state": S("InstanceState"), + "instance_type": S("InstanceType"), + "outpost_arn": S("OutpostArn"), + "launch_time": S("LaunchTime"), + "network_interfaces": S("NetworkInterfaces", default=[]) >> ForallBend(AwsGuardDutyNetworkInterface.mapping), + "platform": S("Platform"), + "product_codes": S("ProductCodes", default=[]) >> ForallBend(AwsGuardDutyProductCode.mapping), + } + availability_zone: Optional[str] = field(default=None, metadata={"description": "The Availability Zone of the EC2 instance."}) # fmt: skip + iam_instance_profile: Optional[AwsGuardDutyIamInstanceProfile] = field(default=None, metadata={"description": "The profile information of the EC2 instance."}) # fmt: skip + image_description: Optional[str] = field(default=None, metadata={"description": "The image description of the EC2 instance."}) # fmt: skip + image_id: Optional[str] = field(default=None, metadata={"description": "The image ID of the EC2 instance."}) # fmt: skip + instance_id: Optional[str] = field(default=None, metadata={"description": "The ID of the EC2 instance."}) # fmt: skip + instance_state: Optional[str] = field(default=None, metadata={"description": "The state of the EC2 instance."}) # fmt: skip + instance_type: Optional[str] = field(default=None, metadata={"description": "The type of the EC2 instance."}) # fmt: skip + outpost_arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) of the Amazon Web Services Outpost. Only applicable to Amazon Web Services Outposts instances."}) # fmt: skip + launch_time: Optional[str] = field(default=None, metadata={"description": "The launch time of the EC2 instance."}) # fmt: skip + network_interfaces: Optional[List[AwsGuardDutyNetworkInterface]] = field(factory=list, metadata={"description": "The elastic network interface information of the EC2 instance."}) # fmt: skip + platform: Optional[str] = field(default=None, metadata={"description": "The platform of the EC2 instance."}) # fmt: skip + product_codes: Optional[List[AwsGuardDutyProductCode]] = field(factory=list, metadata={"description": "The product code of the EC2 instance."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyEksClusterDetails: + kind: ClassVar[str] = "aws_guard_duty_eks_cluster_details" + mapping: ClassVar[Dict[str, Bender]] = { + "name": S("Name"), + "arn": S("Arn"), + "vpc_id": S("VpcId"), + "status": S("Status"), + "created_at": S("CreatedAt"), + } + name: Optional[str] = field(default=None, metadata={"description": "EKS cluster name."}) # fmt: skip + arn: Optional[str] = field(default=None, metadata={"description": "EKS cluster ARN."}) # fmt: skip + vpc_id: Optional[str] = field(default=None, metadata={"description": "The VPC ID to which the EKS cluster is attached."}) # fmt: skip + status: Optional[str] = field(default=None, metadata={"description": "The EKS cluster status."}) # fmt: skip + created_at: Optional[datetime] = field(default=None, metadata={"description": "The timestamp when the EKS cluster was created."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyImpersonatedUser: + kind: ClassVar[str] = "aws_guard_duty_impersonated_user" + mapping: ClassVar[Dict[str, Bender]] = {"username": S("Username"), "groups": S("Groups", default=[])} + username: Optional[str] = field(default=None, metadata={"description": "Information about the username that was being impersonated."}) # fmt: skip + groups: Optional[List[str]] = field(factory=list, metadata={"description": "The group to which the user name belongs."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyKubernetesUserDetails: + kind: ClassVar[str] = "aws_guard_duty_kubernetes_user_details" + mapping: ClassVar[Dict[str, Bender]] = { + "username": S("Username"), + "uid": S("Uid"), + "groups": S("Groups", default=[]), + "session_name": S("SessionName", default=[]), + "impersonated_user": S("ImpersonatedUser") >> Bend(AwsGuardDutyImpersonatedUser.mapping), + } + username: Optional[str] = field(default=None, metadata={"description": "The username of the user who called the Kubernetes API."}) # fmt: skip + uid: Optional[str] = field(default=None, metadata={"description": "The user ID of the user who called the Kubernetes API."}) # fmt: skip + groups: Optional[List[str]] = field(factory=list, metadata={"description": "The groups that include the user who called the Kubernetes API."}) # fmt: skip + session_name: Optional[List[str]] = field(factory=list, metadata={"description": "Entity that assumes the IAM role when Kubernetes RBAC permissions are assigned to that role."}) # fmt: skip + impersonated_user: Optional[AwsGuardDutyImpersonatedUser] = field(default=None, metadata={"description": "Information about the impersonated user."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyVolumeMount: + kind: ClassVar[str] = "aws_guard_duty_volume_mount" + mapping: ClassVar[Dict[str, Bender]] = {"name": S("Name"), "mount_path": S("MountPath")} + name: Optional[str] = field(default=None, metadata={"description": "Volume mount name."}) # fmt: skip + mount_path: Optional[str] = field(default=None, metadata={"description": "Volume mount path."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutySecurityContext: + kind: ClassVar[str] = "aws_guard_duty_security_context" + mapping: ClassVar[Dict[str, Bender]] = { + "privileged": S("Privileged"), + "allow_privilege_escalation": S("AllowPrivilegeEscalation"), + } + privileged: Optional[bool] = field(default=None, metadata={"description": "Whether the container is privileged."}) # fmt: skip + allow_privilege_escalation: Optional[bool] = field(default=None, metadata={"description": "Whether or not a container or a Kubernetes pod is allowed to gain more privileges than its parent process."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyContainer: + kind: ClassVar[str] = "aws_guard_duty_container" + mapping: ClassVar[Dict[str, Bender]] = { + "container_runtime": S("ContainerRuntime"), + "id": S("Id"), + "name": S("Name"), + "image": S("Image"), + "image_prefix": S("ImagePrefix"), + "volume_mounts": S("VolumeMounts", default=[]) >> ForallBend(AwsGuardDutyVolumeMount.mapping), + "security_context": S("SecurityContext") >> Bend(AwsGuardDutySecurityContext.mapping), + } + container_runtime: Optional[str] = field(default=None, metadata={"description": "The container runtime (such as, Docker or containerd) used to run the container."}) # fmt: skip + id: Optional[str] = field(default=None, metadata={"description": "Container ID."}) # fmt: skip + name: Optional[str] = field(default=None, metadata={"description": "Container name."}) # fmt: skip + image: Optional[str] = field(default=None, metadata={"description": "Container image."}) # fmt: skip + image_prefix: Optional[str] = field(default=None, metadata={"description": "Part of the image name before the last slash. For example, imagePrefix for public.ecr.aws/amazonlinux/amazonlinux:latest would be public.ecr.aws/amazonlinux. If the image name is relative and does not have a slash, this field is empty."}) # fmt: skip + volume_mounts: Optional[List[AwsGuardDutyVolumeMount]] = field(factory=list, metadata={"description": "Container volume mounts."}) # fmt: skip + security_context: Optional[AwsGuardDutySecurityContext] = field(default=None, metadata={"description": "Container security context."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyVolume: + kind: ClassVar[str] = "aws_guard_duty_volume" + mapping: ClassVar[Dict[str, Bender]] = {"name": S("Name"), "host_path": S("HostPath", "Path")} + name: Optional[str] = field(default=None, metadata={"description": "Volume name."}) # fmt: skip + host_path: Optional[str] = field(default=None, metadata={"description": "Represents a pre-existing file or directory on the host machine that the volume maps to."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyKubernetesWorkloadDetails: + kind: ClassVar[str] = "aws_guard_duty_kubernetes_workload_details" + mapping: ClassVar[Dict[str, Bender]] = { + "name": S("Name"), + "type": S("Type"), + "uid": S("Uid"), + "namespace": S("Namespace"), + "host_network": S("HostNetwork"), + "containers": S("Containers", default=[]) >> ForallBend(AwsGuardDutyContainer.mapping), + "volumes": S("Volumes", default=[]) >> ForallBend(AwsGuardDutyVolume.mapping), + "service_account_name": S("ServiceAccountName"), + "host_ipc": S("HostIPC"), + "host_pid": S("HostPID"), + } + name: Optional[str] = field(default=None, metadata={"description": "Kubernetes workload name."}) # fmt: skip + type: Optional[str] = field(default=None, metadata={"description": "Kubernetes workload type (e.g. Pod, Deployment, etc.)."}) # fmt: skip + uid: Optional[str] = field(default=None, metadata={"description": "Kubernetes workload ID."}) # fmt: skip + namespace: Optional[str] = field(default=None, metadata={"description": "Kubernetes namespace that the workload is part of."}) # fmt: skip + host_network: Optional[bool] = field(default=None, metadata={"description": "Whether the hostNetwork flag is enabled for the pods included in the workload."}) # fmt: skip + containers: Optional[List[AwsGuardDutyContainer]] = field(factory=list, metadata={"description": "Containers running as part of the Kubernetes workload."}) # fmt: skip + volumes: Optional[List[AwsGuardDutyVolume]] = field(factory=list, metadata={"description": "Volumes used by the Kubernetes workload."}) # fmt: skip + service_account_name: Optional[str] = field(default=None, metadata={"description": "The service account name that is associated with a Kubernetes workload."}) # fmt: skip + host_ipc: Optional[bool] = field(default=None, metadata={"description": "Whether the host IPC flag is enabled for the pods in the workload."}) # fmt: skip + host_pid: Optional[bool] = field(default=None, metadata={"description": "Whether the host PID flag is enabled for the pods in the workload."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyKubernetesDetails: + kind: ClassVar[str] = "aws_guard_duty_kubernetes_details" + mapping: ClassVar[Dict[str, Bender]] = { + "kubernetes_user_details": S("KubernetesUserDetails") >> Bend(AwsGuardDutyKubernetesUserDetails.mapping), + "kubernetes_workload_details": S("KubernetesWorkloadDetails") + >> Bend(AwsGuardDutyKubernetesWorkloadDetails.mapping), + } + kubernetes_user_details: Optional[AwsGuardDutyKubernetesUserDetails] = field(default=None, metadata={"description": "Details about the Kubernetes user involved in a Kubernetes finding."}) # fmt: skip + kubernetes_workload_details: Optional[AwsGuardDutyKubernetesWorkloadDetails] = field(default=None, metadata={"description": "Details about the Kubernetes workload involved in a Kubernetes finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyVolumeDetail: + kind: ClassVar[str] = "aws_guard_duty_volume_detail" + mapping: ClassVar[Dict[str, Bender]] = { + "volume_arn": S("VolumeArn"), + "volume_type": S("VolumeType"), + "device_name": S("DeviceName"), + "volume_size_in_gb": S("VolumeSizeInGB"), + "encryption_type": S("EncryptionType"), + "snapshot_arn": S("SnapshotArn"), + "kms_key_arn": S("KmsKeyArn"), + } + volume_arn: Optional[str] = field(default=None, metadata={"description": "EBS volume ARN information."}) # fmt: skip + volume_type: Optional[str] = field(default=None, metadata={"description": "The EBS volume type."}) # fmt: skip + device_name: Optional[str] = field(default=None, metadata={"description": "The device name for the EBS volume."}) # fmt: skip + volume_size_in_gb: Optional[int] = field(default=None, metadata={"description": "EBS volume size in GB."}) # fmt: skip + encryption_type: Optional[str] = field(default=None, metadata={"description": "EBS volume encryption type."}) # fmt: skip + snapshot_arn: Optional[str] = field(default=None, metadata={"description": "Snapshot ARN of the EBS volume."}) # fmt: skip + kms_key_arn: Optional[str] = field(default=None, metadata={"description": "KMS key ARN used to encrypt the EBS volume."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyEbsVolumeDetails: + kind: ClassVar[str] = "aws_guard_duty_ebs_volume_details" + mapping: ClassVar[Dict[str, Bender]] = { + "scanned_volume_details": S("ScannedVolumeDetails", default=[]) >> ForallBend(AwsGuardDutyVolumeDetail.mapping), + "skipped_volume_details": S("SkippedVolumeDetails", default=[]) >> ForallBend(AwsGuardDutyVolumeDetail.mapping), + } + scanned_volume_details: Optional[List[AwsGuardDutyVolumeDetail]] = field(factory=list, metadata={"description": "List of EBS volumes that were scanned."}) # fmt: skip + skipped_volume_details: Optional[List[AwsGuardDutyVolumeDetail]] = field(factory=list, metadata={"description": "List of EBS volumes that were skipped from the malware scan."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyEcsTaskDetails: + kind: ClassVar[str] = "aws_guard_duty_ecs_task_details" + mapping: ClassVar[Dict[str, Bender]] = { + "arn": S("Arn"), + "definition_arn": S("DefinitionArn"), + "version": S("Version"), + "task_created_at": S("TaskCreatedAt"), + "started_at": S("StartedAt"), + "started_by": S("StartedBy"), + "volumes": S("Volumes", default=[]) >> ForallBend(AwsGuardDutyVolume.mapping), + "containers": S("Containers", default=[]) >> ForallBend(AwsGuardDutyContainer.mapping), + "group": S("Group"), + } + arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) of the task."}) # fmt: skip + definition_arn: Optional[str] = field(default=None, metadata={"description": "The ARN of the task definition that creates the task."}) # fmt: skip + version: Optional[str] = field(default=None, metadata={"description": "The version counter for the task."}) # fmt: skip + task_created_at: Optional[datetime] = field(default=None, metadata={"description": "The Unix timestamp for the time when the task was created."}) # fmt: skip + started_at: Optional[datetime] = field(default=None, metadata={"description": "The Unix timestamp for the time when the task started."}) # fmt: skip + started_by: Optional[str] = field(default=None, metadata={"description": "Contains the tag specified when a task is started."}) # fmt: skip + volumes: Optional[List[AwsGuardDutyVolume]] = field(factory=list, metadata={"description": "The list of data volume definitions for the task."}) # fmt: skip + containers: Optional[List[AwsGuardDutyContainer]] = field(factory=list, metadata={"description": "The containers that's associated with the task."}) # fmt: skip + group: Optional[str] = field(default=None, metadata={"description": "The name of the task group that's associated with the task."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyEcsClusterDetails: + kind: ClassVar[str] = "aws_guard_duty_ecs_cluster_details" + mapping: ClassVar[Dict[str, Bender]] = { + "name": S("Name"), + "arn": S("Arn"), + "status": S("Status"), + "active_services_count": S("ActiveServicesCount"), + "registered_container_instances_count": S("RegisteredContainerInstancesCount"), + "running_tasks_count": S("RunningTasksCount"), + "task_details": S("TaskDetails") >> Bend(AwsGuardDutyEcsTaskDetails.mapping), + } + name: Optional[str] = field(default=None, metadata={"description": "The name of the ECS Cluster."}) # fmt: skip + arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) that identifies the cluster."}) # fmt: skip + status: Optional[str] = field(default=None, metadata={"description": "The status of the ECS cluster."}) # fmt: skip + active_services_count: Optional[int] = field(default=None, metadata={"description": "The number of services that are running on the cluster in an ACTIVE state."}) # fmt: skip + registered_container_instances_count: Optional[int] = field(default=None, metadata={"description": "The number of container instances registered into the cluster."}) # fmt: skip + running_tasks_count: Optional[int] = field(default=None, metadata={"description": "The number of tasks in the cluster that are in the RUNNING state."}) # fmt: skip + task_details: Optional[AwsGuardDutyEcsTaskDetails] = field(default=None, metadata={"description": "Contains information about the details of the ECS Task."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyRdsDbInstanceDetails: + kind: ClassVar[str] = "aws_guard_duty_rds_db_instance_details" + mapping: ClassVar[Dict[str, Bender]] = { + "db_instance_identifier": S("DbInstanceIdentifier"), + "engine": S("Engine"), + "engine_version": S("EngineVersion"), + "db_cluster_identifier": S("DbClusterIdentifier"), + "db_instance_arn": S("DbInstanceArn"), + } + db_instance_identifier: Optional[str] = field(default=None, metadata={"description": "The identifier associated to the database instance that was involved in the finding."}) # fmt: skip + engine: Optional[str] = field(default=None, metadata={"description": "The database engine of the database instance involved in the finding."}) # fmt: skip + engine_version: Optional[str] = field(default=None, metadata={"description": "The version of the database engine that was involved in the finding."}) # fmt: skip + db_cluster_identifier: Optional[str] = field(default=None, metadata={"description": "The identifier of the database cluster that contains the database instance ID involved in the finding."}) # fmt: skip + db_instance_arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) that identifies the database instance involved in the finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyRdsDbUserDetails: + kind: ClassVar[str] = "aws_guard_duty_rds_db_user_details" + mapping: ClassVar[Dict[str, Bender]] = { + "user": S("User"), + "application": S("Application"), + "database": S("Database"), + "ssl": S("Ssl"), + "auth_method": S("AuthMethod"), + } + user: Optional[str] = field(default=None, metadata={"description": "The user name used in the anomalous login attempt."}) # fmt: skip + application: Optional[str] = field(default=None, metadata={"description": "The application name used in the anomalous login attempt."}) # fmt: skip + database: Optional[str] = field(default=None, metadata={"description": "The name of the database instance involved in the anomalous login attempt."}) # fmt: skip + ssl: Optional[str] = field(default=None, metadata={"description": "The version of the Secure Socket Layer (SSL) used for the network."}) # fmt: skip + auth_method: Optional[str] = field(default=None, metadata={"description": "The authentication method used by the user involved in the finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyVpcConfig: + kind: ClassVar[str] = "aws_guard_duty_vpc_config" + mapping: ClassVar[Dict[str, Bender]] = { + "subnet_ids": S("SubnetIds", default=[]), + "vpc_id": S("VpcId"), + "security_groups": S("SecurityGroups", default=[]) >> ForallBend(AwsGuardDutySecurityGroup.mapping), + } + subnet_ids: Optional[List[str]] = field(factory=list, metadata={"description": "The identifiers of the subnets that are associated with your Lambda function."}) # fmt: skip + vpc_id: Optional[str] = field(default=None, metadata={"description": "The identifier of the Amazon Virtual Private Cloud."}) # fmt: skip + security_groups: Optional[List[AwsGuardDutySecurityGroup]] = field(factory=list, metadata={"description": "The identifier of the security group attached to the Lambda function."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyLambdaDetails: + kind: ClassVar[str] = "aws_guard_duty_lambda_details" + mapping: ClassVar[Dict[str, Bender]] = { + "function_arn": S("FunctionArn"), + "function_name": S("FunctionName"), + "description": S("Description"), + "last_modified_at": S("LastModifiedAt"), + "revision_id": S("RevisionId"), + "function_version": S("FunctionVersion"), + "role": S("Role"), + "vpc_config": S("VpcConfig") >> Bend(AwsGuardDutyVpcConfig.mapping), + } + function_arn: Optional[str] = field(default=None, metadata={"description": "Amazon Resource Name (ARN) of the Lambda function."}) # fmt: skip + function_name: Optional[str] = field(default=None, metadata={"description": "Name of the Lambda function."}) # fmt: skip + description: Optional[str] = field(default=None, metadata={"description": "Description of the Lambda function."}) # fmt: skip + last_modified_at: Optional[datetime] = field(default=None, metadata={"description": "The timestamp when the Lambda function was last modified. This field is in the UTC date string format (2023-03-22T19:37:20.168Z)."}) # fmt: skip + revision_id: Optional[str] = field(default=None, metadata={"description": "The revision ID of the Lambda function version."}) # fmt: skip + function_version: Optional[str] = field(default=None, metadata={"description": "The version of the Lambda function."}) # fmt: skip + role: Optional[str] = field(default=None, metadata={"description": "The execution role of the Lambda function."}) # fmt: skip + vpc_config: Optional[AwsGuardDutyVpcConfig] = field(default=None, metadata={"description": "Amazon Virtual Private Cloud configuration details associated with your Lambda function."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyResource: + kind: ClassVar[str] = "aws_guard_duty_resource" + mapping: ClassVar[Dict[str, Bender]] = { + "access_key_details": S("AccessKeyDetails") >> Bend(AwsGuardDutyAccessKeyDetails.mapping), + "s3_bucket_details": S("S3BucketDetails", default=[]) >> ForallBend(AwsGuardDutyS3BucketDetail.mapping), + "instance_details": S("InstanceDetails") >> Bend(AwsGuardDutyInstanceDetails.mapping), + "eks_cluster_details": S("EksClusterDetails") >> Bend(AwsGuardDutyEksClusterDetails.mapping), + "kubernetes_details": S("KubernetesDetails") >> Bend(AwsGuardDutyKubernetesDetails.mapping), + "resource_type": S("ResourceType"), + "ebs_volume_details": S("EbsVolumeDetails") >> Bend(AwsGuardDutyEbsVolumeDetails.mapping), + "ecs_cluster_details": S("EcsClusterDetails") >> Bend(AwsGuardDutyEcsClusterDetails.mapping), + "container_details": S("ContainerDetails") >> Bend(AwsGuardDutyContainer.mapping), + "rds_db_instance_details": S("RdsDbInstanceDetails") >> Bend(AwsGuardDutyRdsDbInstanceDetails.mapping), + "rds_db_user_details": S("RdsDbUserDetails") >> Bend(AwsGuardDutyRdsDbUserDetails.mapping), + "lambda_details": S("LambdaDetails") >> Bend(AwsGuardDutyLambdaDetails.mapping), + } + access_key_details: Optional[AwsGuardDutyAccessKeyDetails] = field(default=None, metadata={"description": "The IAM access key details (user information) of a user that engaged in the activity that prompted GuardDuty to generate a finding."}) # fmt: skip + s3_bucket_details: Optional[List[AwsGuardDutyS3BucketDetail]] = field(factory=list, metadata={"description": "Contains information on the S3 bucket."}) # fmt: skip + instance_details: Optional[AwsGuardDutyInstanceDetails] = field(default=None, metadata={"description": "The information about the EC2 instance associated with the activity that prompted GuardDuty to generate a finding."}) # fmt: skip + eks_cluster_details: Optional[AwsGuardDutyEksClusterDetails] = field(default=None, metadata={"description": "Details about the EKS cluster involved in a Kubernetes finding."}) # fmt: skip + kubernetes_details: Optional[AwsGuardDutyKubernetesDetails] = field(default=None, metadata={"description": "Details about the Kubernetes user and workload involved in a Kubernetes finding."}) # fmt: skip + resource_type: Optional[str] = field(default=None, metadata={"description": "The type of Amazon Web Services resource."}) # fmt: skip + ebs_volume_details: Optional[AwsGuardDutyEbsVolumeDetails] = field(default=None, metadata={"description": "Contains list of scanned and skipped EBS volumes with details."}) # fmt: skip + ecs_cluster_details: Optional[AwsGuardDutyEcsClusterDetails] = field(default=None, metadata={"description": "Contains information about the details of the ECS Cluster."}) # fmt: skip + container_details: Optional[AwsGuardDutyContainer] = field(default=None, metadata={"description": "Details of a container."}) # fmt: skip + rds_db_instance_details: Optional[AwsGuardDutyRdsDbInstanceDetails] = field(default=None, metadata={"description": "Contains information about the database instance to which an anomalous login attempt was made."}) # fmt: skip + rds_db_user_details: Optional[AwsGuardDutyRdsDbUserDetails] = field(default=None, metadata={"description": "Contains information about the user details through which anomalous login attempt was made."}) # fmt: skip + lambda_details: Optional[AwsGuardDutyLambdaDetails] = field(default=None, metadata={"description": "Contains information about the Lambda function that was involved in a finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyCountry: + kind: ClassVar[str] = "aws_guard_duty_country" + mapping: ClassVar[Dict[str, Bender]] = {"country_code": S("CountryCode"), "country_name": S("CountryName")} + country_code: Optional[str] = field(default=None, metadata={"description": "The country code of the remote IP address."}) # fmt: skip + country_name: Optional[str] = field(default=None, metadata={"description": "The country name of the remote IP address."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyGeoLocation: + kind: ClassVar[str] = "aws_guard_duty_geo_location" + mapping: ClassVar[Dict[str, Bender]] = {"lat": S("Lat"), "lon": S("Lon")} + lat: Optional[float] = field(default=None, metadata={"description": "The latitude information of the remote IP address."}) # fmt: skip + lon: Optional[float] = field(default=None, metadata={"description": "The longitude information of the remote IP address."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyOrganization: + kind: ClassVar[str] = "aws_guard_duty_organization" + mapping: ClassVar[Dict[str, Bender]] = {"asn": S("Asn"), "asn_org": S("AsnOrg"), "isp": S("Isp"), "org": S("Org")} + asn: Optional[str] = field(default=None, metadata={"description": "The Autonomous System Number (ASN) of the internet provider of the remote IP address."}) # fmt: skip + asn_org: Optional[str] = field(default=None, metadata={"description": "The organization that registered this ASN."}) # fmt: skip + isp: Optional[str] = field(default=None, metadata={"description": "The ISP information for the internet provider."}) # fmt: skip + org: Optional[str] = field(default=None, metadata={"description": "The name of the internet provider."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyRemoteIpDetails: + kind: ClassVar[str] = "aws_guard_duty_remote_ip_details" + mapping: ClassVar[Dict[str, Bender]] = { + "city": S("City", "CityName"), + "country": S("Country") >> Bend(AwsGuardDutyCountry.mapping), + "geo_location": S("GeoLocation") >> Bend(AwsGuardDutyGeoLocation.mapping), + "ip_address_v4": S("IpAddressV4"), + "ip_address_v6": S("IpAddressV6"), + "organization": S("Organization") >> Bend(AwsGuardDutyOrganization.mapping), + } + city: Optional[str] = field(default=None, metadata={"description": "The city information of the remote IP address."}) # fmt: skip + country: Optional[AwsGuardDutyCountry] = field(default=None, metadata={"description": "The country code of the remote IP address."}) # fmt: skip + geo_location: Optional[AwsGuardDutyGeoLocation] = field(default=None, metadata={"description": "The location information of the remote IP address."}) # fmt: skip + ip_address_v4: Optional[str] = field(default=None, metadata={"description": "The IPv4 remote address of the connection."}) # fmt: skip + ip_address_v6: Optional[str] = field(default=None, metadata={"description": "The IPv6 remote address of the connection."}) # fmt: skip + organization: Optional[AwsGuardDutyOrganization] = field(default=None, metadata={"description": "The ISP organization information of the remote IP address."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyRemoteAccountDetails: + kind: ClassVar[str] = "aws_guard_duty_remote_account_details" + mapping: ClassVar[Dict[str, Bender]] = {"account_id": S("AccountId"), "affiliated": S("Affiliated")} + account_id: Optional[str] = field(default=None, metadata={"description": "The Amazon Web Services account ID of the remote API caller."}) # fmt: skip + affiliated: Optional[bool] = field(default=None, metadata={"description": "Details on whether the Amazon Web Services account of the remote API caller is related to your GuardDuty environment. If this value is True the API caller is affiliated to your account in some way. If it is False the API caller is from outside your environment."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyAwsApiCallAction: + kind: ClassVar[str] = "aws_guard_duty_aws_api_call_action" + mapping: ClassVar[Dict[str, Bender]] = { + "api": S("Api"), + "caller_type": S("CallerType"), + "domain_details": S("DomainDetails", "Domain"), + "error_code": S("ErrorCode"), + "user_agent": S("UserAgent"), + "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), + "service_name": S("ServiceName"), + "remote_account_details": S("RemoteAccountDetails") >> Bend(AwsGuardDutyRemoteAccountDetails.mapping), + "affected_resources": S("AffectedResources"), + } + api: Optional[str] = field(default=None, metadata={"description": "The Amazon Web Services API name."}) # fmt: skip + caller_type: Optional[str] = field(default=None, metadata={"description": "The Amazon Web Services API caller type."}) # fmt: skip + domain_details: Optional[str] = field(default=None, metadata={"description": "The domain information for the Amazon Web Services API call."}) # fmt: skip + error_code: Optional[str] = field(default=None, metadata={"description": "The error code of the failed Amazon Web Services API action."}) # fmt: skip + user_agent: Optional[str] = field(default=None, metadata={"description": "The agent through which the API request was made."}) # fmt: skip + remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "The remote IP information of the connection that initiated the Amazon Web Services API call."}) # fmt: skip + service_name: Optional[str] = field(default=None, metadata={"description": "The Amazon Web Services service name whose API was invoked."}) # fmt: skip + remote_account_details: Optional[AwsGuardDutyRemoteAccountDetails] = field(default=None, metadata={"description": "The details of the Amazon Web Services account that made the API call. This field appears if the call was made from outside your account."}) # fmt: skip + affected_resources: Optional[Dict[str, str]] = field(default=None, metadata={"description": "The details of the Amazon Web Services account that made the API call. This field identifies the resources that were affected by this API call."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyDnsRequestAction: + kind: ClassVar[str] = "aws_guard_duty_dns_request_action" + mapping: ClassVar[Dict[str, Bender]] = { + "domain": S("Domain"), + "protocol": S("Protocol"), + "blocked": S("Blocked"), + "domain_with_suffix": S("DomainWithSuffix"), + } + domain: Optional[str] = field(default=None, metadata={"description": "The domain information for the DNS query."}) # fmt: skip + protocol: Optional[str] = field(default=None, metadata={"description": "The network connection protocol observed in the activity that prompted GuardDuty to generate the finding."}) # fmt: skip + blocked: Optional[bool] = field(default=None, metadata={"description": "Indicates whether the targeted port is blocked."}) # fmt: skip + domain_with_suffix: Optional[str] = field(default=None, metadata={"description": "The second and top level domain involved in the activity that potentially prompted GuardDuty to generate this finding. For a list of top-level and second-level domains, see public suffix list."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyLocalPortDetails: + kind: ClassVar[str] = "aws_guard_duty_local_port_details" + mapping: ClassVar[Dict[str, Bender]] = {"port": S("Port"), "port_name": S("PortName")} + port: Optional[int] = field(default=None, metadata={"description": "The port number of the local connection."}) # fmt: skip + port_name: Optional[str] = field(default=None, metadata={"description": "The port name of the local connection."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyLocalIpDetails: + kind: ClassVar[str] = "aws_guard_duty_local_ip_details" + mapping: ClassVar[Dict[str, Bender]] = {"ip_address_v4": S("IpAddressV4"), "ip_address_v6": S("IpAddressV6")} + ip_address_v4: Optional[str] = field(default=None, metadata={"description": "The IPv4 local address of the connection."}) # fmt: skip + ip_address_v6: Optional[str] = field(default=None, metadata={"description": "The IPv6 local address of the connection."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyRemotePortDetails: + kind: ClassVar[str] = "aws_guard_duty_remote_port_details" + mapping: ClassVar[Dict[str, Bender]] = {"port": S("Port"), "port_name": S("PortName")} + port: Optional[int] = field(default=None, metadata={"description": "The port number of the remote connection."}) # fmt: skip + port_name: Optional[str] = field(default=None, metadata={"description": "The port name of the remote connection."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyNetworkConnectionAction: + kind: ClassVar[str] = "aws_guard_duty_network_connection_action" + mapping: ClassVar[Dict[str, Bender]] = { + "blocked": S("Blocked"), + "connection_direction": S("ConnectionDirection"), + "local_port_details": S("LocalPortDetails") >> Bend(AwsGuardDutyLocalPortDetails.mapping), + "protocol": S("Protocol"), + "local_ip_details": S("LocalIpDetails") >> Bend(AwsGuardDutyLocalIpDetails.mapping), + "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), + "remote_port_details": S("RemotePortDetails") >> Bend(AwsGuardDutyRemotePortDetails.mapping), + } + blocked: Optional[bool] = field(default=None, metadata={"description": "Indicates whether EC2 blocked the network connection to your instance."}) # fmt: skip + connection_direction: Optional[str] = field(default=None, metadata={"description": "The network connection direction."}) # fmt: skip + local_port_details: Optional[AwsGuardDutyLocalPortDetails] = field(default=None, metadata={"description": "The local port information of the connection."}) # fmt: skip + protocol: Optional[str] = field(default=None, metadata={"description": "The network connection protocol."}) # fmt: skip + local_ip_details: Optional[AwsGuardDutyLocalIpDetails] = field(default=None, metadata={"description": "The local IP information of the connection."}) # fmt: skip + remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "The remote IP information of the connection."}) # fmt: skip + remote_port_details: Optional[AwsGuardDutyRemotePortDetails] = field(default=None, metadata={"description": "The remote port information of the connection."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyPortProbeDetail: + kind: ClassVar[str] = "aws_guard_duty_port_probe_detail" + mapping: ClassVar[Dict[str, Bender]] = { + "local_port_details": S("LocalPortDetails") >> Bend(AwsGuardDutyLocalPortDetails.mapping), + "local_ip_details": S("LocalIpDetails") >> Bend(AwsGuardDutyLocalIpDetails.mapping), + "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), + } + local_port_details: Optional[AwsGuardDutyLocalPortDetails] = field(default=None, metadata={"description": "The local port information of the connection."}) # fmt: skip + local_ip_details: Optional[AwsGuardDutyLocalIpDetails] = field(default=None, metadata={"description": "The local IP information of the connection."}) # fmt: skip + remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "The remote IP information of the connection."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyPortProbeAction: + kind: ClassVar[str] = "aws_guard_duty_port_probe_action" + mapping: ClassVar[Dict[str, Bender]] = { + "blocked": S("Blocked"), + "port_probe_details": S("PortProbeDetails", default=[]) >> ForallBend(AwsGuardDutyPortProbeDetail.mapping), + } + blocked: Optional[bool] = field(default=None, metadata={"description": "Indicates whether EC2 blocked the port probe to the instance, such as with an ACL."}) # fmt: skip + port_probe_details: Optional[List[AwsGuardDutyPortProbeDetail]] = field(factory=list, metadata={"description": "A list of objects related to port probe details."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyKubernetesApiCallAction: + kind: ClassVar[str] = "aws_guard_duty_kubernetes_api_call_action" + mapping: ClassVar[Dict[str, Bender]] = { + "request_uri": S("RequestUri"), + "verb": S("Verb"), + "source_ips": S("SourceIps", default=[]), + "user_agent": S("UserAgent"), + "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), + "status_code": S("StatusCode"), + "parameters": S("Parameters"), + "resource": S("Resource"), + "subresource": S("Subresource"), + "namespace": S("Namespace"), + "resource_name": S("ResourceName"), + } + request_uri: Optional[str] = field(default=None, metadata={"description": "The Kubernetes API request URI."}) # fmt: skip + verb: Optional[str] = field(default=None, metadata={"description": "The Kubernetes API request HTTP verb."}) # fmt: skip + source_ips: Optional[List[str]] = field(factory=list, metadata={"description": "The IP of the Kubernetes API caller and the IPs of any proxies or load balancers between the caller and the API endpoint."}) # fmt: skip + user_agent: Optional[str] = field(default=None, metadata={"description": "The user agent of the caller of the Kubernetes API."}) # fmt: skip + remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "Contains information about the remote IP address of the connection."}) # fmt: skip + status_code: Optional[int] = field(default=None, metadata={"description": "The resulting HTTP response code of the Kubernetes API call action."}) # fmt: skip + parameters: Optional[str] = field(default=None, metadata={"description": "Parameters related to the Kubernetes API call action."}) # fmt: skip + resource: Optional[str] = field(default=None, metadata={"description": "The resource component in the Kubernetes API call action."}) # fmt: skip + subresource: Optional[str] = field(default=None, metadata={"description": "The name of the sub-resource in the Kubernetes API call action."}) # fmt: skip + namespace: Optional[str] = field(default=None, metadata={"description": "The name of the namespace where the Kubernetes API call action takes place."}) # fmt: skip + resource_name: Optional[str] = field(default=None, metadata={"description": "The name of the resource in the Kubernetes API call action."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyLoginAttribute: + kind: ClassVar[str] = "aws_guard_duty_login_attribute" + mapping: ClassVar[Dict[str, Bender]] = { + "user": S("User"), + "application": S("Application"), + "failed_login_attempts": S("FailedLoginAttempts"), + "successful_login_attempts": S("SuccessfulLoginAttempts"), + } + user: Optional[str] = field(default=None, metadata={"description": "Indicates the user name which attempted to log in."}) # fmt: skip + application: Optional[str] = field(default=None, metadata={"description": "Indicates the application name used to attempt log in."}) # fmt: skip + failed_login_attempts: Optional[int] = field(default=None, metadata={"description": "Represents the sum of failed (unsuccessful) login attempts made to establish a connection to the database instance."}) # fmt: skip + successful_login_attempts: Optional[int] = field(default=None, metadata={"description": "Represents the sum of successful connections (a correct combination of login attributes) made to the database instance by the actor."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyRdsLoginAttemptAction: + kind: ClassVar[str] = "aws_guard_duty_rds_login_attempt_action" + mapping: ClassVar[Dict[str, Bender]] = { + "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), + "login_attributes": S("LoginAttributes", default=[]) >> ForallBend(AwsGuardDutyLoginAttribute.mapping), + } + remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "Contains information about the remote IP address of the connection."}) # fmt: skip + login_attributes: Optional[List[AwsGuardDutyLoginAttribute]] = field(factory=list, metadata={"description": "Indicates the login attributes used in the login attempt."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyKubernetesPermissionCheckedDetails: + kind: ClassVar[str] = "aws_guard_duty_kubernetes_permission_checked_details" + mapping: ClassVar[Dict[str, Bender]] = { + "verb": S("Verb"), + "resource": S("Resource"), + "namespace": S("Namespace"), + "allowed": S("Allowed"), + } + verb: Optional[str] = field(default=None, metadata={"description": "The verb component of the Kubernetes API call. For example, when you check whether or not you have the permission to call the CreatePod API, the verb component will be Create."}) # fmt: skip + resource: Optional[str] = field(default=None, metadata={"description": "The Kubernetes resource with which your Kubernetes API call will interact."}) # fmt: skip + namespace: Optional[str] = field(default=None, metadata={"description": "The namespace where the Kubernetes API action will take place."}) # fmt: skip + allowed: Optional[bool] = field(default=None, metadata={"description": "Information whether the user has the permission to call the Kubernetes API."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyKubernetesRoleBindingDetails: + kind: ClassVar[str] = "aws_guard_duty_kubernetes_role_binding_details" + mapping: ClassVar[Dict[str, Bender]] = { + "role_kind": S("Kind"), + "name": S("Name"), + "uid": S("Uid"), + "role_ref_name": S("RoleRefName"), + "role_ref_kind": S("RoleRefKind"), + } + role_kind: Optional[str] = field(default=None, metadata={"description": "The kind of the role. For role binding, this value will be RoleBinding."}) # fmt: skip + name: Optional[str] = field(default=None, metadata={"description": "The name of the RoleBinding."}) # fmt: skip + uid: Optional[str] = field(default=None, metadata={"description": "The unique identifier of the role binding."}) # fmt: skip + role_ref_name: Optional[str] = field(default=None, metadata={"description": "The name of the role being referenced. This must match the name of the Role or ClusterRole that you want to bind to."}) # fmt: skip + role_ref_kind: Optional[str] = field(default=None, metadata={"description": "The type of the role being referenced. This could be either Role or ClusterRole."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyKubernetesRoleDetails: + kind: ClassVar[str] = "aws_guard_duty_kubernetes_role_details" + mapping: ClassVar[Dict[str, Bender]] = {"role_kind": S("Kind"), "name": S("Name"), "uid": S("Uid")} + role_kind: Optional[str] = field(default=None, metadata={"description": "The kind of role. For this API, the value of kind will be Role."}) # fmt: skip + name: Optional[str] = field(default=None, metadata={"description": "The name of the Kubernetes role."}) # fmt: skip + uid: Optional[str] = field(default=None, metadata={"description": "The unique identifier of the Kubernetes role name."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyAction: + kind: ClassVar[str] = "aws_guard_duty_action" + mapping: ClassVar[Dict[str, Bender]] = { + "action_type": S("ActionType"), + "aws_api_call_action": S("AwsApiCallAction") >> Bend(AwsGuardDutyAwsApiCallAction.mapping), + "dns_request_action": S("DnsRequestAction") >> Bend(AwsGuardDutyDnsRequestAction.mapping), + "network_connection_action": S("NetworkConnectionAction") >> Bend(AwsGuardDutyNetworkConnectionAction.mapping), + "port_probe_action": S("PortProbeAction") >> Bend(AwsGuardDutyPortProbeAction.mapping), + "kubernetes_api_call_action": S("KubernetesApiCallAction") >> Bend(AwsGuardDutyKubernetesApiCallAction.mapping), + "rds_login_attempt_action": S("RdsLoginAttemptAction") >> Bend(AwsGuardDutyRdsLoginAttemptAction.mapping), + "kubernetes_permission_checked_details": S("KubernetesPermissionCheckedDetails") + >> Bend(AwsGuardDutyKubernetesPermissionCheckedDetails.mapping), + "kubernetes_role_binding_details": S("KubernetesRoleBindingDetails") + >> Bend(AwsGuardDutyKubernetesRoleBindingDetails.mapping), + "kubernetes_role_details": S("KubernetesRoleDetails") >> Bend(AwsGuardDutyKubernetesRoleDetails.mapping), + } + action_type: Optional[str] = field(default=None, metadata={"description": "The GuardDuty finding activity type."}) # fmt: skip + aws_api_call_action: Optional[AwsGuardDutyAwsApiCallAction] = field(default=None, metadata={"description": "Information about the AWS_API_CALL action described in this finding."}) # fmt: skip + dns_request_action: Optional[AwsGuardDutyDnsRequestAction] = field(default=None, metadata={"description": "Information about the DNS_REQUEST action described in this finding."}) # fmt: skip + network_connection_action: Optional[AwsGuardDutyNetworkConnectionAction] = field(default=None, metadata={"description": "Information about the NETWORK_CONNECTION action described in this finding."}) # fmt: skip + port_probe_action: Optional[AwsGuardDutyPortProbeAction] = field(default=None, metadata={"description": "Information about the PORT_PROBE action described in this finding."}) # fmt: skip + kubernetes_api_call_action: Optional[AwsGuardDutyKubernetesApiCallAction] = field(default=None, metadata={"description": "Information about the Kubernetes API call action described in this finding."}) # fmt: skip + rds_login_attempt_action: Optional[AwsGuardDutyRdsLoginAttemptAction] = field(default=None, metadata={"description": "Information about RDS_LOGIN_ATTEMPT action described in this finding."}) # fmt: skip + kubernetes_permission_checked_details: Optional[AwsGuardDutyKubernetesPermissionCheckedDetails] = field(default=None, metadata={"description": "Information whether the user has the permission to use a specific Kubernetes API."}) # fmt: skip + kubernetes_role_binding_details: Optional[AwsGuardDutyKubernetesRoleBindingDetails] = field(default=None, metadata={"description": "Information about the role binding that grants the permission defined in a Kubernetes role."}) # fmt: skip + kubernetes_role_details: Optional[AwsGuardDutyKubernetesRoleDetails] = field(default=None, metadata={"description": "Information about the Kubernetes role name and role type."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyThreatIntelligenceDetail: + kind: ClassVar[str] = "aws_guard_duty_threat_intelligence_detail" + mapping: ClassVar[Dict[str, Bender]] = { + "threat_list_name": S("ThreatListName"), + "threat_names": S("ThreatNames", default=[]), + "threat_file_sha256": S("ThreatFileSha256"), + } + threat_list_name: Optional[str] = field(default=None, metadata={"description": "The name of the threat intelligence list that triggered the finding."}) # fmt: skip + threat_names: Optional[List[str]] = field(factory=list, metadata={"description": "A list of names of the threats in the threat intelligence list that triggered the finding."}) # fmt: skip + threat_file_sha256: Optional[str] = field(default=None, metadata={"description": "SHA256 of the file that generated the finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyEvidence: + kind: ClassVar[str] = "aws_guard_duty_evidence" + mapping: ClassVar[Dict[str, Bender]] = { + "threat_intelligence_details": S("ThreatIntelligenceDetails", default=[]) + >> ForallBend(AwsGuardDutyThreatIntelligenceDetail.mapping) + } + threat_intelligence_details: Optional[List[AwsGuardDutyThreatIntelligenceDetail]] = field(factory=list, metadata={"description": "A list of threat intelligence details related to the evidence."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyServiceAdditionalInfo: + kind: ClassVar[str] = "aws_guard_duty_service_additional_info" + mapping: ClassVar[Dict[str, Bender]] = {"value": S("Value"), "type": S("Type")} + value: Optional[str] = field(default=None, metadata={"description": "This field specifies the value of the additional information."}) # fmt: skip + type: Optional[str] = field(default=None, metadata={"description": "Describes the type of the additional information."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyScannedItemCount: + kind: ClassVar[str] = "aws_guard_duty_scanned_item_count" + mapping: ClassVar[Dict[str, Bender]] = {"total_gb": S("TotalGb"), "files": S("Files"), "volumes": S("Volumes")} + total_gb: Optional[int] = field(default=None, metadata={"description": "Total GB of files scanned for malware."}) # fmt: skip + files: Optional[int] = field(default=None, metadata={"description": "Number of files scanned."}) # fmt: skip + volumes: Optional[int] = field(default=None, metadata={"description": "Total number of scanned volumes."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyHighestSeverityThreatDetails: + kind: ClassVar[str] = "aws_guard_duty_highest_severity_threat_details" + mapping: ClassVar[Dict[str, Bender]] = { + "severity": S("Severity"), + "threat_name": S("ThreatName"), + "count": S("Count"), + } + severity: Optional[str] = field(default=None, metadata={"description": "Severity level of the highest severity threat detected."}) # fmt: skip + threat_name: Optional[str] = field(default=None, metadata={"description": "Threat name of the highest severity threat detected as part of the malware scan."}) # fmt: skip + count: Optional[int] = field(default=None, metadata={"description": "Total number of infected files with the highest severity threat detected."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyScanFilePath: + kind: ClassVar[str] = "aws_guard_duty_scan_file_path" + mapping: ClassVar[Dict[str, Bender]] = { + "file_path": S("FilePath"), + "volume_arn": S("VolumeArn"), + "hash": S("Hash"), + "file_name": S("FileName"), + } + file_path: Optional[str] = field(default=None, metadata={"description": "The file path of the infected file."}) # fmt: skip + volume_arn: Optional[str] = field(default=None, metadata={"description": "EBS volume ARN details of the infected file."}) # fmt: skip + hash: Optional[str] = field(default=None, metadata={"description": "The hash value of the infected file."}) # fmt: skip + file_name: Optional[str] = field(default=None, metadata={"description": "File name of the infected file."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyScanThreatName: + kind: ClassVar[str] = "aws_guard_duty_scan_threat_name" + mapping: ClassVar[Dict[str, Bender]] = { + "name": S("Name"), + "severity": S("Severity"), + "item_count": S("ItemCount"), + "file_paths": S("FilePaths", default=[]) >> ForallBend(AwsGuardDutyScanFilePath.mapping), + } + name: Optional[str] = field(default=None, metadata={"description": "The name of the identified threat."}) # fmt: skip + severity: Optional[str] = field(default=None, metadata={"description": "Severity of threat identified as part of the malware scan."}) # fmt: skip + item_count: Optional[int] = field(default=None, metadata={"description": "Total number of files infected with given threat."}) # fmt: skip + file_paths: Optional[List[AwsGuardDutyScanFilePath]] = field(factory=list, metadata={"description": "List of infected files in EBS volume with details."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyThreatDetectedByName: + kind: ClassVar[str] = "aws_guard_duty_threat_detected_by_name" + mapping: ClassVar[Dict[str, Bender]] = { + "item_count": S("ItemCount"), + "unique_threat_name_count": S("UniqueThreatNameCount"), + "shortened": S("Shortened"), + "threat_names": S("ThreatNames", default=[]) >> ForallBend(AwsGuardDutyScanThreatName.mapping), + } + item_count: Optional[int] = field(default=None, metadata={"description": "Total number of infected files identified."}) # fmt: skip + unique_threat_name_count: Optional[int] = field(default=None, metadata={"description": "Total number of unique threats by name identified, as part of the malware scan."}) # fmt: skip + shortened: Optional[bool] = field(default=None, metadata={"description": "Flag to determine if the finding contains every single infected file-path and/or every threat."}) # fmt: skip + threat_names: Optional[List[AwsGuardDutyScanThreatName]] = field(factory=list, metadata={"description": "List of identified threats with details, organized by threat name."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyScanDetections: + kind: ClassVar[str] = "aws_guard_duty_scan_detections" + mapping: ClassVar[Dict[str, Bender]] = { + "scanned_item_count": S("ScannedItemCount") >> Bend(AwsGuardDutyScannedItemCount.mapping), + "threats_detected_item_count": S("ThreatsDetectedItemCount", "Files"), + "highest_severity_threat_details": S("HighestSeverityThreatDetails") + >> Bend(AwsGuardDutyHighestSeverityThreatDetails.mapping), + "threat_detected_by_name": S("ThreatDetectedByName") >> Bend(AwsGuardDutyThreatDetectedByName.mapping), + } + scanned_item_count: Optional[AwsGuardDutyScannedItemCount] = field(default=None, metadata={"description": "Total number of scanned files."}) # fmt: skip + threats_detected_item_count: Optional[int] = field(default=None, metadata={"description": "Total number of infected files."}) # fmt: skip + highest_severity_threat_details: Optional[AwsGuardDutyHighestSeverityThreatDetails] = field(default=None, metadata={"description": "Details of the highest severity threat detected during malware scan and number of infected files."}) # fmt: skip + threat_detected_by_name: Optional[AwsGuardDutyThreatDetectedByName] = field(default=None, metadata={"description": "Contains details about identified threats organized by threat name."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyEbsVolumeScanDetails: + kind: ClassVar[str] = "aws_guard_duty_ebs_volume_scan_details" + mapping: ClassVar[Dict[str, Bender]] = { + "scan_id": S("ScanId"), + "scan_started_at": S("ScanStartedAt"), + "scan_completed_at": S("ScanCompletedAt"), + "trigger_finding_id": S("TriggerFindingId"), + "sources": S("Sources", default=[]), + "scan_detections": S("ScanDetections") >> Bend(AwsGuardDutyScanDetections.mapping), + "scan_type": S("ScanType"), + } + scan_id: Optional[str] = field(default=None, metadata={"description": "Unique Id of the malware scan that generated the finding."}) # fmt: skip + scan_started_at: Optional[datetime] = field(default=None, metadata={"description": "Returns the start date and time of the malware scan."}) # fmt: skip + scan_completed_at: Optional[datetime] = field(default=None, metadata={"description": "Returns the completion date and time of the malware scan."}) # fmt: skip + trigger_finding_id: Optional[str] = field(default=None, metadata={"description": "GuardDuty finding ID that triggered a malware scan."}) # fmt: skip + sources: Optional[List[str]] = field(factory=list, metadata={"description": "Contains list of threat intelligence sources used to detect threats."}) # fmt: skip + scan_detections: Optional[AwsGuardDutyScanDetections] = field(default=None, metadata={"description": "Contains a complete view providing malware scan result details."}) # fmt: skip + scan_type: Optional[str] = field(default=None, metadata={"description": "Specifies the scan type that invoked the malware scan."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyLineageObject: + kind: ClassVar[str] = "aws_guard_duty_lineage_object" + mapping: ClassVar[Dict[str, Bender]] = { + "start_time": S("StartTime"), + "namespace_pid": S("NamespacePid"), + "user_id": S("UserId"), + "name": S("Name"), + "pid": S("Pid"), + "uuid": S("Uuid"), + "executable_path": S("ExecutablePath"), + "euid": S("Euid"), + "parent_uuid": S("ParentUuid"), + } + start_time: Optional[datetime] = field(default=None, metadata={"description": "The time when the process started. This is in UTC format."}) # fmt: skip + namespace_pid: Optional[int] = field(default=None, metadata={"description": "The process ID of the child process."}) # fmt: skip + user_id: Optional[int] = field(default=None, metadata={"description": "The user ID of the user that executed the process."}) # fmt: skip + name: Optional[str] = field(default=None, metadata={"description": "The name of the process."}) # fmt: skip + pid: Optional[int] = field(default=None, metadata={"description": "The ID of the process."}) # fmt: skip + uuid: Optional[str] = field(default=None, metadata={"description": "The unique ID assigned to the process by GuardDuty."}) # fmt: skip + executable_path: Optional[str] = field(default=None, metadata={"description": "The absolute path of the process executable file."}) # fmt: skip + euid: Optional[int] = field(default=None, metadata={"description": "The effective user ID that was used to execute the process."}) # fmt: skip + parent_uuid: Optional[str] = field(default=None, metadata={"description": "The unique ID of the parent process. This ID is assigned to the parent process by GuardDuty."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyProcessDetails: + kind: ClassVar[str] = "aws_guard_duty_process_details" + mapping: ClassVar[Dict[str, Bender]] = { + "name": S("Name"), + "executable_path": S("ExecutablePath"), + "executable_sha256": S("ExecutableSha256"), + "namespace_pid": S("NamespacePid"), + "pwd": S("Pwd"), + "pid": S("Pid"), + "start_time": S("StartTime"), + "uuid": S("Uuid"), + "parent_uuid": S("ParentUuid"), + "user": S("User"), + "user_id": S("UserId"), + "euid": S("Euid"), + "lineage": S("Lineage", default=[]) >> ForallBend(AwsGuardDutyLineageObject.mapping), + } + name: Optional[str] = field(default=None, metadata={"description": "The name of the process."}) # fmt: skip + executable_path: Optional[str] = field(default=None, metadata={"description": "The absolute path of the process executable file."}) # fmt: skip + executable_sha256: Optional[str] = field(default=None, metadata={"description": "The SHA256 hash of the process executable."}) # fmt: skip + namespace_pid: Optional[int] = field(default=None, metadata={"description": "The ID of the child process."}) # fmt: skip + pwd: Optional[str] = field(default=None, metadata={"description": "The present working directory of the process."}) # fmt: skip + pid: Optional[int] = field(default=None, metadata={"description": "The ID of the process."}) # fmt: skip + start_time: Optional[datetime] = field(default=None, metadata={"description": "The time when the process started. This is in UTC format."}) # fmt: skip + uuid: Optional[str] = field(default=None, metadata={"description": "The unique ID assigned to the process by GuardDuty."}) # fmt: skip + parent_uuid: Optional[str] = field(default=None, metadata={"description": "The unique ID of the parent process. This ID is assigned to the parent process by GuardDuty."}) # fmt: skip + user: Optional[str] = field(default=None, metadata={"description": "The user that executed the process."}) # fmt: skip + user_id: Optional[int] = field(default=None, metadata={"description": "The unique ID of the user that executed the process."}) # fmt: skip + euid: Optional[int] = field(default=None, metadata={"description": "The effective user ID of the user that executed the process."}) # fmt: skip + lineage: Optional[List[AwsGuardDutyLineageObject]] = field(factory=list, metadata={"description": "Information about the process's lineage."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyRuntimeContext: + kind: ClassVar[str] = "aws_guard_duty_runtime_context" + mapping: ClassVar[Dict[str, Bender]] = { + "modifying_process": S("ModifyingProcess") >> Bend(AwsGuardDutyProcessDetails.mapping), + "modified_at": S("ModifiedAt"), + "script_path": S("ScriptPath"), + "library_path": S("LibraryPath"), + "ld_preload_value": S("LdPreloadValue"), + "socket_path": S("SocketPath"), + "runc_binary_path": S("RuncBinaryPath"), + "release_agent_path": S("ReleaseAgentPath"), + "mount_source": S("MountSource"), + "mount_target": S("MountTarget"), + "file_system_type": S("FileSystemType"), + "flags": S("Flags", default=[]), + "module_name": S("ModuleName"), + "module_file_path": S("ModuleFilePath"), + "module_sha256": S("ModuleSha256"), + "shell_history_file_path": S("ShellHistoryFilePath"), + "target_process": S("TargetProcess") >> Bend(AwsGuardDutyProcessDetails.mapping), + "address_family": S("AddressFamily"), + "iana_protocol_number": S("IanaProtocolNumber"), + "memory_regions": S("MemoryRegions", default=[]), + "tool_name": S("ToolName"), + "tool_category": S("ToolCategory"), + "service_name": S("ServiceName"), + "command_line_example": S("CommandLineExample"), + "threat_file_path": S("ThreatFilePath"), + } + modifying_process: Optional[AwsGuardDutyProcessDetails] = field(default=None, metadata={"description": "Information about the process that modified the current process. This is available for multiple finding types."}) # fmt: skip + modified_at: Optional[datetime] = field(default=None, metadata={"description": "The timestamp at which the process modified the current process. The timestamp is in UTC date string format."}) # fmt: skip + script_path: Optional[str] = field(default=None, metadata={"description": "The path to the script that was executed."}) # fmt: skip + library_path: Optional[str] = field(default=None, metadata={"description": "The path to the new library that was loaded."}) # fmt: skip + ld_preload_value: Optional[str] = field(default=None, metadata={"description": "The value of the LD_PRELOAD environment variable."}) # fmt: skip + socket_path: Optional[str] = field(default=None, metadata={"description": "The path to the docket socket that was accessed."}) # fmt: skip + runc_binary_path: Optional[str] = field(default=None, metadata={"description": "The path to the leveraged runc implementation."}) # fmt: skip + release_agent_path: Optional[str] = field(default=None, metadata={"description": "The path in the container that modified the release agent file."}) # fmt: skip + mount_source: Optional[str] = field(default=None, metadata={"description": "The path on the host that is mounted by the container."}) # fmt: skip + mount_target: Optional[str] = field(default=None, metadata={"description": "The path in the container that is mapped to the host directory."}) # fmt: skip + file_system_type: Optional[str] = field(default=None, metadata={"description": "Represents the type of mounted fileSystem."}) # fmt: skip + flags: Optional[List[str]] = field(factory=list, metadata={"description": "Represents options that control the behavior of a runtime operation or action. For example, a filesystem mount operation may contain a read-only flag."}) # fmt: skip + module_name: Optional[str] = field(default=None, metadata={"description": "The name of the module loaded into the kernel."}) # fmt: skip + module_file_path: Optional[str] = field(default=None, metadata={"description": "The path to the module loaded into the kernel."}) # fmt: skip + module_sha256: Optional[str] = field(default=None, metadata={"description": "The SHA256 hash of the module."}) # fmt: skip + shell_history_file_path: Optional[str] = field(default=None, metadata={"description": "The path to the modified shell history file."}) # fmt: skip + target_process: Optional[AwsGuardDutyProcessDetails] = field(default=None, metadata={"description": "Information about the process that had its memory overwritten by the current process."}) # fmt: skip + address_family: Optional[str] = field(default=None, metadata={"description": "Represents the communication protocol associated with the address. For example, the address family AF_INET is used for IP version of 4 protocol."}) # fmt: skip + iana_protocol_number: Optional[int] = field(default=None, metadata={"description": "Specifies a particular protocol within the address family. Usually there is a single protocol in address families. For example, the address family AF_INET only has the IP protocol."}) # fmt: skip + memory_regions: Optional[List[str]] = field(factory=list, metadata={"description": "Specifies the Region of a process's address space such as stack and heap."}) # fmt: skip + tool_name: Optional[str] = field(default=None, metadata={"description": "Name of the potentially suspicious tool."}) # fmt: skip + tool_category: Optional[str] = field(default=None, metadata={"description": "Category that the tool belongs to. Some of the examples are Backdoor Tool, Pentest Tool, Network Scanner, and Network Sniffer."}) # fmt: skip + service_name: Optional[str] = field(default=None, metadata={"description": "Name of the security service that has been potentially disabled."}) # fmt: skip + command_line_example: Optional[str] = field(default=None, metadata={"description": "Example of the command line involved in the suspicious activity."}) # fmt: skip + threat_file_path: Optional[str] = field(default=None, metadata={"description": "The suspicious file path for which the threat intelligence details were found."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyRuntimeDetails: + kind: ClassVar[str] = "aws_guard_duty_runtime_details" + mapping: ClassVar[Dict[str, Bender]] = { + "process": S("Process") >> Bend(AwsGuardDutyProcessDetails.mapping), + "context": S("Context") >> Bend(AwsGuardDutyRuntimeContext.mapping), + } + process: Optional[AwsGuardDutyProcessDetails] = field(default=None, metadata={"description": "Information about the observed process."}) # fmt: skip + context: Optional[AwsGuardDutyRuntimeContext] = field(default=None, metadata={"description": "Additional information about the suspicious activity."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyAnomalyUnusual: + kind: ClassVar[str] = "aws_guard_duty_anomaly_unusual" + mapping: ClassVar[Dict[str, Bender]] = {"behavior": S("Behavior")} + behavior: Optional[Dict[str, Any]] = field(default=None, metadata={"description": "The behavior of the anomalous activity that caused GuardDuty to generate the finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyAnomaly: + kind: ClassVar[str] = "aws_guard_duty_anomaly" + mapping: ClassVar[Dict[str, Bender]] = { + "profiles": S("Profiles"), + "unusual": S("Unusual") >> Bend(AwsGuardDutyAnomalyUnusual.mapping), + } + profiles: Optional[Dict[str, Any]] = field(default=None, metadata={"description": "Information about the types of profiles."}) # fmt: skip + unusual: Optional[AwsGuardDutyAnomalyUnusual] = field(default=None, metadata={"description": "Information about the behavior of the anomalies."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyDetection: + kind: ClassVar[str] = "aws_guard_duty_detection" + mapping: ClassVar[Dict[str, Bender]] = {"anomaly": S("Anomaly") >> Bend(AwsGuardDutyAnomaly.mapping)} + anomaly: Optional[AwsGuardDutyAnomaly] = field(default=None, metadata={"description": "The details about the anomalous activity that caused GuardDuty to generate the finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyItemPath: + kind: ClassVar[str] = "aws_guard_duty_item_path" + mapping: ClassVar[Dict[str, Bender]] = {"nested_item_path": S("NestedItemPath"), "hash": S("Hash")} + nested_item_path: Optional[str] = field(default=None, metadata={"description": "The nested item path where the infected file was found."}) # fmt: skip + hash: Optional[str] = field(default=None, metadata={"description": "The hash value of the infected resource."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyThreat: + kind: ClassVar[str] = "aws_guard_duty_threat" + mapping: ClassVar[Dict[str, Bender]] = { + "name": S("Name"), + "source": S("Source"), + "item_paths": S("ItemPaths", default=[]) >> ForallBend(AwsGuardDutyItemPath.mapping), + } + name: Optional[str] = field(default=None, metadata={"description": "Name of the detected threat that caused GuardDuty to generate this finding."}) # fmt: skip + source: Optional[str] = field(default=None, metadata={"description": "Source of the threat that generated this finding."}) # fmt: skip + item_paths: Optional[List[AwsGuardDutyItemPath]] = field(factory=list, metadata={"description": "Information about the nested item path and hash of the protected resource."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyMalwareScanDetails: + kind: ClassVar[str] = "aws_guard_duty_malware_scan_details" + mapping: ClassVar[Dict[str, Bender]] = { + "threats": S("Threats", default=[]) >> ForallBend(AwsGuardDutyThreat.mapping) + } + threats: Optional[List[AwsGuardDutyThreat]] = field(factory=list, metadata={"description": "Information about the detected threats associated with the generated GuardDuty finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyService: + kind: ClassVar[str] = "aws_guard_duty_service" + mapping: ClassVar[Dict[str, Bender]] = { + "action": S("Action") >> Bend(AwsGuardDutyAction.mapping), + "evidence": S("Evidence") >> Bend(AwsGuardDutyEvidence.mapping), + "archived": S("Archived"), + "count": S("Count"), + "detector_id": S("DetectorId"), + "event_first_seen": S("EventFirstSeen"), + "event_last_seen": S("EventLastSeen"), + "resource_role": S("ResourceRole"), + "service_name": S("ServiceName"), + "user_feedback": S("UserFeedback"), + "additional_info": S("AdditionalInfo") >> Bend(AwsGuardDutyServiceAdditionalInfo.mapping), + "feature_name": S("FeatureName"), + "ebs_volume_scan_details": S("EbsVolumeScanDetails") >> Bend(AwsGuardDutyEbsVolumeScanDetails.mapping), + "runtime_details": S("RuntimeDetails") >> Bend(AwsGuardDutyRuntimeDetails.mapping), + "detection": S("Detection") >> Bend(AwsGuardDutyDetection.mapping), + "malware_scan_details": S("MalwareScanDetails") >> Bend(AwsGuardDutyMalwareScanDetails.mapping), + } + action: Optional[AwsGuardDutyAction] = field(default=None, metadata={"description": "Information about the activity that is described in a finding."}) # fmt: skip + evidence: Optional[AwsGuardDutyEvidence] = field(default=None, metadata={"description": "An evidence object associated with the service."}) # fmt: skip + archived: Optional[bool] = field(default=None, metadata={"description": "Indicates whether this finding is archived."}) # fmt: skip + count: Optional[int] = field(default=None, metadata={"description": "The total count of the occurrences of this finding type."}) # fmt: skip + detector_id: Optional[str] = field(default=None, metadata={"description": "The detector ID for the GuardDuty service."}) # fmt: skip + event_first_seen: Optional[str] = field(default=None, metadata={"description": "The first-seen timestamp of the activity that prompted GuardDuty to generate this finding."}) # fmt: skip + event_last_seen: Optional[str] = field(default=None, metadata={"description": "The last-seen timestamp of the activity that prompted GuardDuty to generate this finding."}) # fmt: skip + resource_role: Optional[str] = field(default=None, metadata={"description": "The resource role information for this finding."}) # fmt: skip + service_name: Optional[str] = field(default=None, metadata={"description": "The name of the Amazon Web Services service (GuardDuty) that generated a finding."}) # fmt: skip + user_feedback: Optional[str] = field(default=None, metadata={"description": "Feedback that was submitted about the finding."}) # fmt: skip + additional_info: Optional[AwsGuardDutyServiceAdditionalInfo] = field(default=None, metadata={"description": "Contains additional information about the generated finding."}) # fmt: skip + feature_name: Optional[str] = field(default=None, metadata={"description": "The name of the feature that generated a finding."}) # fmt: skip + ebs_volume_scan_details: Optional[AwsGuardDutyEbsVolumeScanDetails] = field(default=None, metadata={"description": "Returns details from the malware scan that created a finding."}) # fmt: skip + runtime_details: Optional[AwsGuardDutyRuntimeDetails] = field(default=None, metadata={"description": "Information about the process and any required context values for a specific finding"}) # fmt: skip + detection: Optional[AwsGuardDutyDetection] = field(default=None, metadata={"description": "Contains information about the detected unusual behavior."}) # fmt: skip + malware_scan_details: Optional[AwsGuardDutyMalwareScanDetails] = field(default=None, metadata={"description": "Returns details from the malware scan that generated a GuardDuty finding."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): + kind: ClassVar[str] = "aws_guard_duty_finding" + # Collected via AwsGuardDutyDetector() + mapping: ClassVar[Dict[str, Bender]] = { + "id": S("Id"), + "name": S("Title"), + "mtime": S("UpdatedAt"), + "ctime": S("CreatedAt"), + "account_id": S("AccountId"), + "arn": S("Arn"), + "confidence": S("Confidence"), + "created_at": S("CreatedAt"), + "description": S("Description"), + "partition": S("Partition"), + "finding_region": S("Region"), + "finding_resource": S("Resource") >> Bend(AwsGuardDutyResource.mapping), + "schema_version": S("SchemaVersion"), + "finding_service": S("Service") >> Bend(AwsGuardDutyService.mapping), + "severity": S("Severity"), + "title": S("Title"), + "type": S("Type"), + "updated_at": S("UpdatedAt"), + "detector_id": S("DetectorID"), + } + account_id: Optional[str] = field(default=None, metadata={"description": "The ID of the account in which the finding was generated."}) # fmt: skip + confidence: Optional[float] = field(default=None, metadata={"description": "The confidence score for the finding."}) # fmt: skip + created_at: Optional[str] = field(default=None, metadata={"description": "The time and date when the finding was created."}) # fmt: skip + description: Optional[str] = field(default=None, metadata={"description": "The description of the finding."}) # fmt: skip + partition: Optional[str] = field(default=None, metadata={"description": "The partition associated with the finding."}) # fmt: skip + finding_region: Optional[str] = field(default=None, metadata={"description": "The Region where the finding was generated."}) # fmt: skip + finding_resource: Optional[AwsGuardDutyResource] = field(default=None, metadata={"description": "Contains information about the Amazon Web Services resource associated with the activity that prompted GuardDuty to generate a finding."}) # fmt: skip + schema_version: Optional[str] = field(default=None, metadata={"description": "The version of the schema used for the finding."}) # fmt: skip + finding_service: Optional[AwsGuardDutyService] = field(default=None, metadata={"description": "Contains additional information about the generated finding."}) # fmt: skip + severity: Optional[float] = field(default=None, metadata={"description": "The severity of the finding."}) # fmt: skip + title: Optional[str] = field(default=None, metadata={"description": "The title of the finding."}) # fmt: skip + type: Optional[str] = field(default=None, metadata={"description": "The type of finding."}) # fmt: skip + updated_at: Optional[str] = field(default=None, metadata={"description": "The time and date when the finding was last updated."}) # fmt: skip + detector_id: Optional[str] = field(default=None, metadata={"description": "The ID of the Guard Duty detector."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyKubernetesConfigurationResult: + kind: ClassVar[str] = "aws_guard_duty_kubernetes_configuration_result" + mapping: ClassVar[Dict[str, Bender]] = {"audit_logs": S("AuditLogs", "Status")} + audit_logs: Optional[str] = field(default=None, metadata={"description": "Describes whether Kubernetes audit logs are enabled as a data source."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyEbsVolumesResult: + kind: ClassVar[str] = "aws_guard_duty_ebs_volumes_result" + mapping: ClassVar[Dict[str, Bender]] = {"status": S("Status"), "reason": S("Reason")} + status: Optional[str] = field(default=None, metadata={"description": "Describes whether scanning EBS volumes is enabled as a data source."}) # fmt: skip + reason: Optional[str] = field(default=None, metadata={"description": "Specifies the reason why scanning EBS volumes (Malware Protection) was not enabled as a data source."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyScanEc2InstanceWithFindingsResult: + kind: ClassVar[str] = "aws_guard_duty_scan_ec2_instance_with_findings_result" + mapping: ClassVar[Dict[str, Bender]] = { + "ebs_volumes": S("EbsVolumes") >> Bend(AwsGuardDutyEbsVolumesResult.mapping) + } + ebs_volumes: Optional[AwsGuardDutyEbsVolumesResult] = field(default=None, metadata={"description": "Describes the configuration of scanning EBS volumes as a data source."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyMalwareProtectionConfigurationResult: + kind: ClassVar[str] = "aws_guard_duty_malware_protection_configuration_result" + mapping: ClassVar[Dict[str, Bender]] = { + "scan_ec2_instance_with_findings": S("ScanEc2InstanceWithFindings") + >> Bend(AwsGuardDutyScanEc2InstanceWithFindingsResult.mapping), + "service_role": S("ServiceRole"), + } + scan_ec2_instance_with_findings: Optional[AwsGuardDutyScanEc2InstanceWithFindingsResult] = field(default=None, metadata={"description": "Describes the configuration of Malware Protection for EC2 instances with findings."}) # fmt: skip + service_role: Optional[str] = field(default=None, metadata={"description": "The GuardDuty Malware Protection service role."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyDataSourceConfigurationsResult: + kind: ClassVar[str] = "aws_guard_duty_data_source_configurations_result" + mapping: ClassVar[Dict[str, Bender]] = { + "cloud_trail": S("CloudTrail", "Status"), + "dns_logs": S("DNSLogs", "Status"), + "flow_logs": S("FlowLogs", "Status"), + "s3_logs": S("S3Logs", "Status"), + "kubernetes": S("Kubernetes") >> Bend(AwsGuardDutyKubernetesConfigurationResult.mapping), + "malware_protection": S("MalwareProtection") >> Bend(AwsGuardDutyMalwareProtectionConfigurationResult.mapping), + } + cloud_trail: Optional[str] = field(default=None, metadata={"description": "An object that contains information on the status of CloudTrail as a data source."}) # fmt: skip + dns_logs: Optional[str] = field(default=None, metadata={"description": "An object that contains information on the status of DNS logs as a data source."}) # fmt: skip + flow_logs: Optional[str] = field(default=None, metadata={"description": "An object that contains information on the status of VPC flow logs as a data source."}) # fmt: skip + s3_logs: Optional[str] = field(default=None, metadata={"description": "An object that contains information on the status of S3 Data event logs as a data source."}) # fmt: skip + kubernetes: Optional[AwsGuardDutyKubernetesConfigurationResult] = field(default=None, metadata={"description": "An object that contains information on the status of all Kubernetes data sources."}) # fmt: skip + malware_protection: Optional[AwsGuardDutyMalwareProtectionConfigurationResult] = field(default=None, metadata={"description": "Describes the configuration of Malware Protection data sources."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyDetectorAdditionalConfigurationResult: + kind: ClassVar[str] = "aws_guard_duty_detector_additional_configuration_result" + mapping: ClassVar[Dict[str, Bender]] = {"name": S("Name"), "status": S("Status"), "updated_at": S("UpdatedAt")} + name: Optional[str] = field(default=None, metadata={"description": "Name of the additional configuration."}) # fmt: skip + status: Optional[str] = field(default=None, metadata={"description": "Status of the additional configuration."}) # fmt: skip + updated_at: Optional[datetime] = field(default=None, metadata={"description": "The timestamp at which the additional configuration was last updated. This is in UTC format."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyDetectorFeatureConfigurationResult: + kind: ClassVar[str] = "aws_guard_duty_detector_feature_configuration_result" + mapping: ClassVar[Dict[str, Bender]] = { + "name": S("Name"), + "status": S("Status"), + "updated_at": S("UpdatedAt"), + "additional_configuration": S("AdditionalConfiguration", default=[]) + >> ForallBend(AwsGuardDutyDetectorAdditionalConfigurationResult.mapping), + } + name: Optional[str] = field(default=None, metadata={"description": "Indicates the name of the feature that can be enabled for the detector."}) # fmt: skip + status: Optional[str] = field(default=None, metadata={"description": "Indicates the status of the feature that is enabled for the detector."}) # fmt: skip + updated_at: Optional[datetime] = field(default=None, metadata={"description": "The timestamp at which the feature object was updated."}) # fmt: skip + additional_configuration: Optional[List[AwsGuardDutyDetectorAdditionalConfigurationResult]] = field(factory=list, metadata={"description": "Additional configuration for a resource."}) # fmt: skip + + +@define(eq=False, slots=False) +class AwsGuardDutyDetector(AwsResource): + kind: ClassVar[str] = "aws_guard_duty_detector" + api_spec: ClassVar[AwsApiSpec] = AwsApiSpec("guardduty", "list-detectors", "DetectorIds") + mapping: ClassVar[Dict[str, Bender]] = { + "id": S("Id"), + "tags": S("Tags"), + "name": S("Id"), + "ctime": S("CreatedAt"), + "mtime": S("UpdatedAt"), + "created_at": S("CreatedAt"), + "finding_publishing_frequency": S("FindingPublishingFrequency"), + "service_role": S("ServiceRole"), + "status": S("Status"), + "updated_at": S("UpdatedAt"), + "data_sources": S("DataSources") >> Bend(AwsGuardDutyDataSourceConfigurationsResult.mapping), + "features": S("Features", default=[]) >> ForallBend(AwsGuardDutyDetectorFeatureConfigurationResult.mapping), + } + created_at: Optional[str] = field(default=None, metadata={"description": "The timestamp of when the detector was created."}) # fmt: skip + finding_publishing_frequency: Optional[str] = field(default=None, metadata={"description": "The publishing frequency of the finding."}) # fmt: skip + service_role: Optional[str] = field(default=None, metadata={"description": "The GuardDuty service role."}) # fmt: skip + status: Optional[str] = field(default=None, metadata={"description": "The detector status."}) # fmt: skip + updated_at: Optional[str] = field(default=None, metadata={"description": "The last-updated timestamp for the detector."}) # fmt: skip + data_sources: Optional[AwsGuardDutyDataSourceConfigurationsResult] = field(default=None, metadata={"description": "Describes which data sources are enabled for the detector."}) # fmt: skip + features: Optional[List[AwsGuardDutyDetectorFeatureConfigurationResult]] = field(factory=list, metadata={"description": "Describes the features that have been enabled for the detector."}) # fmt: skip + + @classmethod + def collect(cls: Type[AwsResource], json: List[Json], builder: GraphBuilder) -> None: + def collect_findings(detector: AwsGuardDutyDetector) -> None: + if finding_ids := builder.client.list( + service_name, + "list-findings", + "FindingIds", + DetectorId=detector.id, + ): + for finding in builder.client.list( + service_name, "get-findings", "Findings", DetectorId=detector.id, FindingIds=finding_ids + ): + if finding.get("AccountId", None) == builder.account.id: + if instance := AwsGuardDutyFinding.from_api(finding, builder): + builder.add_node(instance, result) + + for detector_id in json: + for result in builder.client.list( + service_name, + "get-detector", + DetectorId=detector_id, + ): + result["Id"] = detector_id + if instance := AwsGuardDutyDetector.from_api(result, builder): + builder.add_node(instance, result) + builder.submit_work(service_name, collect_findings, instance) + + +resources: List[Type[AwsResource]] = [AwsGuardDutyFinding, AwsGuardDutyDetector] diff --git a/plugins/aws/tools/aws_model_gen.py b/plugins/aws/tools/aws_model_gen.py index cd9f3df32f..fedc4c670c 100644 --- a/plugins/aws/tools/aws_model_gen.py +++ b/plugins/aws/tools/aws_model_gen.py @@ -985,6 +985,14 @@ def default_imports() -> str: # prefix="Bedrock", # ) ], + "guardduty": [ + AwsFixModel( + api_action="get-findings", + result_property="Findings", + result_shape="GetFindingsResponse", + prefix="GuardDuty", + ), + ], "inspector2": [ # AwsFixModel( # api_action="list-findings", From aab0cb5f2baf9bdfe891897776e0d58e2bdd54c7 Mon Sep 17 00:00:00 2001 From: Kirill Date: Wed, 16 Oct 2024 15:37:07 +0000 Subject: [PATCH 02/18] feat: added new way to collect --- plugins/aws/fix_plugin_aws/collector.py | 2 + .../aws/fix_plugin_aws/resource/guardduty.py | 195 ++++-------------- .../files/guardduty/list-detectors.json | 6 + .../files/guardduty/list-findings__foo.json | 8 + 4 files changed, 60 insertions(+), 151 deletions(-) create mode 100644 plugins/aws/test/resources/files/guardduty/list-detectors.json create mode 100644 plugins/aws/test/resources/files/guardduty/list-findings__foo.json diff --git a/plugins/aws/fix_plugin_aws/collector.py b/plugins/aws/fix_plugin_aws/collector.py index d55d96ee53..01c27acd92 100644 --- a/plugins/aws/fix_plugin_aws/collector.py +++ b/plugins/aws/fix_plugin_aws/collector.py @@ -50,6 +50,7 @@ backup, bedrock, scp, + guardduty, inspector, ) from fix_plugin_aws.resource.base import ( @@ -105,6 +106,7 @@ + elb.resources + elbv2.resources + glacier.resources + + guardduty.resources + kinesis.resources + kms.resources + lambda_.resources diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 0d78064ab8..2a7c742e1e 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -1,16 +1,18 @@ -from datetime import datetime -from typing import ClassVar, Dict, List, Optional, Tuple, Type, Any +from datetime import datetime, timezone +from typing import ClassVar, Dict, List, Optional, Type, Any +import logging + from attrs import define, field +from boto3.exceptions import Boto3Error -from fix_plugin_aws.aws_client import AwsClient from fix_plugin_aws.resource.base import AwsApiSpec, AwsResource, GraphBuilder -from fix_plugin_aws.utils import ToDict from fixlib.baseresources import ModelReference, PhantomBaseResource from fixlib.graph import Graph -from fixlib.json_bender import S, Bend, Bender, ForallBend, bend +from fixlib.json_bender import F, S, AsInt, Bend, Bender, ForallBend, bend from fixlib.types import Json +from fixlib.utils import chunks, utc_str - +log = logging.getLogger("fix.plugins.aws") service_name = "guardduty" @@ -1207,178 +1209,69 @@ class AwsGuardDutyService: @define(eq=False, slots=False) -class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): +class AwsGuardDutyFinding(AwsResource): kind: ClassVar[str] = "aws_guard_duty_finding" # Collected via AwsGuardDutyDetector() mapping: ClassVar[Dict[str, Bender]] = { "id": S("Id"), "name": S("Title"), - "mtime": S("UpdatedAt"), - "ctime": S("CreatedAt"), + "mtime": S("UpdatedAt") >> AsInt() >> F(lambda x: utc_str(datetime.fromtimestamp(x, timezone.utc))), + "ctime": S("CreatedAt") >> AsInt() >> F(lambda x: utc_str(datetime.fromtimestamp(x, timezone.utc))), "account_id": S("AccountId"), "arn": S("Arn"), "confidence": S("Confidence"), - "created_at": S("CreatedAt"), "description": S("Description"), "partition": S("Partition"), "finding_region": S("Region"), "finding_resource": S("Resource") >> Bend(AwsGuardDutyResource.mapping), "schema_version": S("SchemaVersion"), "finding_service": S("Service") >> Bend(AwsGuardDutyService.mapping), - "severity": S("Severity"), + "finding_severity": S("Severity"), "title": S("Title"), "type": S("Type"), - "updated_at": S("UpdatedAt"), - "detector_id": S("DetectorID"), } account_id: Optional[str] = field(default=None, metadata={"description": "The ID of the account in which the finding was generated."}) # fmt: skip confidence: Optional[float] = field(default=None, metadata={"description": "The confidence score for the finding."}) # fmt: skip - created_at: Optional[str] = field(default=None, metadata={"description": "The time and date when the finding was created."}) # fmt: skip description: Optional[str] = field(default=None, metadata={"description": "The description of the finding."}) # fmt: skip partition: Optional[str] = field(default=None, metadata={"description": "The partition associated with the finding."}) # fmt: skip finding_region: Optional[str] = field(default=None, metadata={"description": "The Region where the finding was generated."}) # fmt: skip finding_resource: Optional[AwsGuardDutyResource] = field(default=None, metadata={"description": "Contains information about the Amazon Web Services resource associated with the activity that prompted GuardDuty to generate a finding."}) # fmt: skip schema_version: Optional[str] = field(default=None, metadata={"description": "The version of the schema used for the finding."}) # fmt: skip finding_service: Optional[AwsGuardDutyService] = field(default=None, metadata={"description": "Contains additional information about the generated finding."}) # fmt: skip - severity: Optional[float] = field(default=None, metadata={"description": "The severity of the finding."}) # fmt: skip + finding_severity: Optional[float] = field(default=None, metadata={"description": "The severity of the finding."}) # fmt: skip title: Optional[str] = field(default=None, metadata={"description": "The title of the finding."}) # fmt: skip type: Optional[str] = field(default=None, metadata={"description": "The type of finding."}) # fmt: skip - updated_at: Optional[str] = field(default=None, metadata={"description": "The time and date when the finding was last updated."}) # fmt: skip - detector_id: Optional[str] = field(default=None, metadata={"description": "The ID of the Guard Duty detector."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyKubernetesConfigurationResult: - kind: ClassVar[str] = "aws_guard_duty_kubernetes_configuration_result" - mapping: ClassVar[Dict[str, Bender]] = {"audit_logs": S("AuditLogs", "Status")} - audit_logs: Optional[str] = field(default=None, metadata={"description": "Describes whether Kubernetes audit logs are enabled as a data source."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyEbsVolumesResult: - kind: ClassVar[str] = "aws_guard_duty_ebs_volumes_result" - mapping: ClassVar[Dict[str, Bender]] = {"status": S("Status"), "reason": S("Reason")} - status: Optional[str] = field(default=None, metadata={"description": "Describes whether scanning EBS volumes is enabled as a data source."}) # fmt: skip - reason: Optional[str] = field(default=None, metadata={"description": "Specifies the reason why scanning EBS volumes (Malware Protection) was not enabled as a data source."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyScanEc2InstanceWithFindingsResult: - kind: ClassVar[str] = "aws_guard_duty_scan_ec2_instance_with_findings_result" - mapping: ClassVar[Dict[str, Bender]] = { - "ebs_volumes": S("EbsVolumes") >> Bend(AwsGuardDutyEbsVolumesResult.mapping) - } - ebs_volumes: Optional[AwsGuardDutyEbsVolumesResult] = field(default=None, metadata={"description": "Describes the configuration of scanning EBS volumes as a data source."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyMalwareProtectionConfigurationResult: - kind: ClassVar[str] = "aws_guard_duty_malware_protection_configuration_result" - mapping: ClassVar[Dict[str, Bender]] = { - "scan_ec2_instance_with_findings": S("ScanEc2InstanceWithFindings") - >> Bend(AwsGuardDutyScanEc2InstanceWithFindingsResult.mapping), - "service_role": S("ServiceRole"), - } - scan_ec2_instance_with_findings: Optional[AwsGuardDutyScanEc2InstanceWithFindingsResult] = field(default=None, metadata={"description": "Describes the configuration of Malware Protection for EC2 instances with findings."}) # fmt: skip - service_role: Optional[str] = field(default=None, metadata={"description": "The GuardDuty Malware Protection service role."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyDataSourceConfigurationsResult: - kind: ClassVar[str] = "aws_guard_duty_data_source_configurations_result" - mapping: ClassVar[Dict[str, Bender]] = { - "cloud_trail": S("CloudTrail", "Status"), - "dns_logs": S("DNSLogs", "Status"), - "flow_logs": S("FlowLogs", "Status"), - "s3_logs": S("S3Logs", "Status"), - "kubernetes": S("Kubernetes") >> Bend(AwsGuardDutyKubernetesConfigurationResult.mapping), - "malware_protection": S("MalwareProtection") >> Bend(AwsGuardDutyMalwareProtectionConfigurationResult.mapping), - } - cloud_trail: Optional[str] = field(default=None, metadata={"description": "An object that contains information on the status of CloudTrail as a data source."}) # fmt: skip - dns_logs: Optional[str] = field(default=None, metadata={"description": "An object that contains information on the status of DNS logs as a data source."}) # fmt: skip - flow_logs: Optional[str] = field(default=None, metadata={"description": "An object that contains information on the status of VPC flow logs as a data source."}) # fmt: skip - s3_logs: Optional[str] = field(default=None, metadata={"description": "An object that contains information on the status of S3 Data event logs as a data source."}) # fmt: skip - kubernetes: Optional[AwsGuardDutyKubernetesConfigurationResult] = field(default=None, metadata={"description": "An object that contains information on the status of all Kubernetes data sources."}) # fmt: skip - malware_protection: Optional[AwsGuardDutyMalwareProtectionConfigurationResult] = field(default=None, metadata={"description": "Describes the configuration of Malware Protection data sources."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyDetectorAdditionalConfigurationResult: - kind: ClassVar[str] = "aws_guard_duty_detector_additional_configuration_result" - mapping: ClassVar[Dict[str, Bender]] = {"name": S("Name"), "status": S("Status"), "updated_at": S("UpdatedAt")} - name: Optional[str] = field(default=None, metadata={"description": "Name of the additional configuration."}) # fmt: skip - status: Optional[str] = field(default=None, metadata={"description": "Status of the additional configuration."}) # fmt: skip - updated_at: Optional[datetime] = field(default=None, metadata={"description": "The timestamp at which the additional configuration was last updated. This is in UTC format."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyDetectorFeatureConfigurationResult: - kind: ClassVar[str] = "aws_guard_duty_detector_feature_configuration_result" - mapping: ClassVar[Dict[str, Bender]] = { - "name": S("Name"), - "status": S("Status"), - "updated_at": S("UpdatedAt"), - "additional_configuration": S("AdditionalConfiguration", default=[]) - >> ForallBend(AwsGuardDutyDetectorAdditionalConfigurationResult.mapping), - } - name: Optional[str] = field(default=None, metadata={"description": "Indicates the name of the feature that can be enabled for the detector."}) # fmt: skip - status: Optional[str] = field(default=None, metadata={"description": "Indicates the status of the feature that is enabled for the detector."}) # fmt: skip - updated_at: Optional[datetime] = field(default=None, metadata={"description": "The timestamp at which the feature object was updated."}) # fmt: skip - additional_configuration: Optional[List[AwsGuardDutyDetectorAdditionalConfigurationResult]] = field(factory=list, metadata={"description": "Additional configuration for a resource."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyDetector(AwsResource): - kind: ClassVar[str] = "aws_guard_duty_detector" - api_spec: ClassVar[AwsApiSpec] = AwsApiSpec("guardduty", "list-detectors", "DetectorIds") - mapping: ClassVar[Dict[str, Bender]] = { - "id": S("Id"), - "tags": S("Tags"), - "name": S("Id"), - "ctime": S("CreatedAt"), - "mtime": S("UpdatedAt"), - "created_at": S("CreatedAt"), - "finding_publishing_frequency": S("FindingPublishingFrequency"), - "service_role": S("ServiceRole"), - "status": S("Status"), - "updated_at": S("UpdatedAt"), - "data_sources": S("DataSources") >> Bend(AwsGuardDutyDataSourceConfigurationsResult.mapping), - "features": S("Features", default=[]) >> ForallBend(AwsGuardDutyDetectorFeatureConfigurationResult.mapping), - } - created_at: Optional[str] = field(default=None, metadata={"description": "The timestamp of when the detector was created."}) # fmt: skip - finding_publishing_frequency: Optional[str] = field(default=None, metadata={"description": "The publishing frequency of the finding."}) # fmt: skip - service_role: Optional[str] = field(default=None, metadata={"description": "The GuardDuty service role."}) # fmt: skip - status: Optional[str] = field(default=None, metadata={"description": "The detector status."}) # fmt: skip - updated_at: Optional[str] = field(default=None, metadata={"description": "The last-updated timestamp for the detector."}) # fmt: skip - data_sources: Optional[AwsGuardDutyDataSourceConfigurationsResult] = field(default=None, metadata={"description": "Describes which data sources are enabled for the detector."}) # fmt: skip - features: Optional[List[AwsGuardDutyDetectorFeatureConfigurationResult]] = field(factory=list, metadata={"description": "Describes the features that have been enabled for the detector."}) # fmt: skip @classmethod - def collect(cls: Type[AwsResource], json: List[Json], builder: GraphBuilder) -> None: - def collect_findings(detector: AwsGuardDutyDetector) -> None: - if finding_ids := builder.client.list( - service_name, - "list-findings", - "FindingIds", - DetectorId=detector.id, - ): - for finding in builder.client.list( - service_name, "get-findings", "Findings", DetectorId=detector.id, FindingIds=finding_ids - ): - if finding.get("AccountId", None) == builder.account.id: - if instance := AwsGuardDutyFinding.from_api(finding, builder): - builder.add_node(instance, result) - - for detector_id in json: - for result in builder.client.list( + def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: + try: + detector_ids = builder.client.list( service_name, - "get-detector", - DetectorId=detector_id, - ): - result["Id"] = detector_id - if instance := AwsGuardDutyDetector.from_api(result, builder): - builder.add_node(instance, result) - builder.submit_work(service_name, collect_findings, instance) - - -resources: List[Type[AwsResource]] = [AwsGuardDutyFinding, AwsGuardDutyDetector] + "list-detectors", + "DetectorIds", + ) + for detector_id in detector_ids: + finding_ids = builder.client.list( + service_name, + "list-findings", + "FindingIds", + DetectorId=detector_id, + ) + for chunk_ids in chunks(finding_ids, 50): + for finding in builder.client.list( + service_name, "get-findings", "Findings", DetectorId=detector_id, FindingIds=chunk_ids + ): + if finding.get("AccountId", None) == builder.account.id: + if instance := AwsGuardDutyFinding.from_api(finding, builder): + builder.add_node(instance, finding) + except Boto3Error as e: + msg = f"Error while collecting {cls.__name__} in region {builder.region.name}: {e}" + builder.core_feedback.error(msg, log) + raise + except Exception as e: + msg = f"Error while collecting {cls.__name__} in region {builder.region.name}: {e}" + builder.core_feedback.info(msg, log) + raise + + +resources: List[Type[AwsResource]] = [AwsGuardDutyFinding] diff --git a/plugins/aws/test/resources/files/guardduty/list-detectors.json b/plugins/aws/test/resources/files/guardduty/list-detectors.json new file mode 100644 index 0000000000..db316d3234 --- /dev/null +++ b/plugins/aws/test/resources/files/guardduty/list-detectors.json @@ -0,0 +1,6 @@ +{ + "DetectorIds": [ + "foo" + ], + "NextToken": "foo" +} \ No newline at end of file diff --git a/plugins/aws/test/resources/files/guardduty/list-findings__foo.json b/plugins/aws/test/resources/files/guardduty/list-findings__foo.json new file mode 100644 index 0000000000..356f170ca5 --- /dev/null +++ b/plugins/aws/test/resources/files/guardduty/list-findings__foo.json @@ -0,0 +1,8 @@ +{ + "FindingIds": [ + "foo", + "foo", + "foo" + ], + "NextToken": "foo" +} \ No newline at end of file From f8470057d1c0125a1254fba83e77b6a72596efeb Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 17 Oct 2024 11:21:19 +0000 Subject: [PATCH 03/18] feat: added tests --- plugins/aws/fix_plugin_aws/collector.py | 1 + .../aws/fix_plugin_aws/resource/guardduty.py | 78 +- plugins/aws/test/collector_test.py | 4 +- .../guardduty/get-findings__foo_foo.json | 789 ++++++++++++++++++ .../files/guardduty/list-findings__foo.json | 2 - plugins/aws/test/resources/guardduty.py | 7 + plugins/aws/tools/aws_model_gen.py | 4 +- 7 files changed, 873 insertions(+), 12 deletions(-) create mode 100644 plugins/aws/test/resources/files/guardduty/get-findings__foo_foo.json create mode 100644 plugins/aws/test/resources/guardduty.py diff --git a/plugins/aws/fix_plugin_aws/collector.py b/plugins/aws/fix_plugin_aws/collector.py index 01c27acd92..d1de013277 100644 --- a/plugins/aws/fix_plugin_aws/collector.py +++ b/plugins/aws/fix_plugin_aws/collector.py @@ -381,6 +381,7 @@ def rm_leaf_nodes(cls: Any, ignore_kinds: Optional[Type[Any]] = None, check_pred self.graph.remove_node(node) rm_leaf_nodes(bedrock.AwsBedrockFoundationModel, check_pred=False) + rm_leaf_nodes(guardduty.AwsGuardDutyFinding, check_pred=False) # remove regions that are not in use self.graph.remove_recursively(builder.nodes(AwsRegion, lambda r: r.compute_region_in_use(builder) is False)) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 2a7c742e1e..86a4674dcd 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -5,10 +5,15 @@ from attrs import define, field from boto3.exceptions import Boto3Error -from fix_plugin_aws.resource.base import AwsApiSpec, AwsResource, GraphBuilder -from fixlib.baseresources import ModelReference, PhantomBaseResource -from fixlib.graph import Graph -from fixlib.json_bender import F, S, AsInt, Bend, Bender, ForallBend, bend +from fix_plugin_aws.resource.base import AwsResource, GraphBuilder +from fix_plugin_aws.resource.ec2 import AwsEc2Instance, AwsEc2Volume +from fix_plugin_aws.resource.ecs import AwsEcsCluster +from fix_plugin_aws.resource.eks import AwsEksCluster +from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction +from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance +from fix_plugin_aws.resource.s3 import AwsS3Bucket +from fixlib.baseresources import PhantomBaseResource +from fixlib.json_bender import F, S, AsInt, Bend, Bender, ForallBend from fixlib.types import Json from fixlib.utils import chunks, utc_str @@ -1255,11 +1260,17 @@ def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: service_name, "list-findings", "FindingIds", + expected_errors=["BadRequestException"], DetectorId=detector_id, ) - for chunk_ids in chunks(finding_ids, 50): + for chunk_ids in chunks(finding_ids, 49): for finding in builder.client.list( - service_name, "get-findings", "Findings", DetectorId=detector_id, FindingIds=chunk_ids + service_name, + "get-findings", + "Findings", + expected_errors=["BadRequestException"], + DetectorId=detector_id, + FindingIds=chunk_ids, ): if finding.get("AccountId", None) == builder.account.id: if instance := AwsGuardDutyFinding.from_api(finding, builder): @@ -1273,5 +1284,60 @@ def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: builder.core_feedback.info(msg, log) raise + def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: + resource = self.finding_resource + if resource: + for s3_bucket_detail in resource.s3_bucket_details or []: + if s3_bucket_detail.name: + builder.add_edge(self, clazz=AwsS3Bucket, name=s3_bucket_detail.name) + if resource.instance_details and resource.instance_details.instance_id: + builder.add_edge( + self, + clazz=AwsEc2Instance, + id=resource.instance_details.instance_id, + ) + if resource.eks_cluster_details and resource.eks_cluster_details.arn: + builder.add_edge( + self, + clazz=AwsEksCluster, + arn=resource.eks_cluster_details.arn, + ) + + if resource.ebs_volume_details: + for vol_detail in resource.ebs_volume_details.scanned_volume_details or []: + if vol_detail.volume_arn: + builder.add_edge(self, clazz=AwsEc2Volume, arn=vol_detail.volume_arn) + for vol_detail in resource.ebs_volume_details.skipped_volume_details or []: + if vol_detail.volume_arn: + builder.add_edge(self, clazz=AwsEc2Volume, arn=vol_detail.volume_arn) + + if resource.ecs_cluster_details and resource.ecs_cluster_details.arn: + builder.add_edge( + self, + clazz=AwsEcsCluster, + arn=resource.ecs_cluster_details.arn, + ) + + if resource.rds_db_instance_details: + if resource.rds_db_instance_details.db_instance_arn: + builder.add_edge( + self, + clazz=AwsRdsInstance, + arn=resource.rds_db_instance_details.db_instance_arn, + ) + if resource.rds_db_instance_details.db_cluster_identifier: + builder.add_edge( + self, + clazz=AwsRdsCluster, + id=resource.rds_db_instance_details.db_cluster_identifier, + ) + + if resource.lambda_details and resource.lambda_details.function_name: + builder.add_edge( + self, + clazz=AwsLambdaFunction, + name=resource.lambda_details.function_name, + ) + resources: List[Type[AwsResource]] = [AwsGuardDutyFinding] diff --git a/plugins/aws/test/collector_test.py b/plugins/aws/test/collector_test.py index 9de124602e..c7d1c7bb7c 100644 --- a/plugins/aws/test/collector_test.py +++ b/plugins/aws/test/collector_test.py @@ -37,8 +37,8 @@ def count_kind(clazz: Type[AwsResource]) -> int: # make sure all threads have been joined assert len(threading.enumerate()) == 1 # ensure the correct number of nodes and edges - assert count_kind(AwsResource) == 260 - assert len(account_collector.graph.edges) == 574 + assert count_kind(AwsResource) == 262 + assert len(account_collector.graph.edges) == 577 assert len(account_collector.graph.deferred_edges) == 2 for node in account_collector.graph.nodes: if isinstance(node, AwsRegion): diff --git a/plugins/aws/test/resources/files/guardduty/get-findings__foo_foo.json b/plugins/aws/test/resources/files/guardduty/get-findings__foo_foo.json new file mode 100644 index 0000000000..1b4dc2bfd4 --- /dev/null +++ b/plugins/aws/test/resources/files/guardduty/get-findings__foo_foo.json @@ -0,0 +1,789 @@ +{ + "Findings": [ + { + "AccountId": "test", + "Arn": "foo", + "Confidence": 1.234, + "CreatedAt": "foo", + "Description": "foo", + "Id": "foo", + "Partition": "foo", + "Region": "foo", + "Resource": { + "AccessKeyDetails": { + "AccessKeyId": "foo", + "PrincipalId": "foo", + "UserName": "foo", + "UserType": "foo" + }, + "S3BucketDetails": [ + { + "Arn": "foo", + "Name": "foo", + "Type": "foo", + "CreatedAt": "2024-10-17T11:10:43Z", + "Owner": { + "Id": "foo" + }, + "Tags": [ + { + "Key": "foo", + "Value": "foo" + } + ], + "DefaultServerSideEncryption": { + "EncryptionType": "foo", + "KmsMasterKeyArn": "foo" + }, + "PublicAccess": { + "PermissionConfiguration": { + "BucketLevelPermissions": { + "AccessControlList": { + "AllowsPublicReadAccess": true, + "AllowsPublicWriteAccess": true + }, + "BucketPolicy": { + "AllowsPublicReadAccess": true, + "AllowsPublicWriteAccess": true + }, + "BlockPublicAccess": { + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true, + "BlockPublicAcls": true, + "BlockPublicPolicy": true + } + }, + "AccountLevelPermissions": { + "BlockPublicAccess": { + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true, + "BlockPublicAcls": true, + "BlockPublicPolicy": true + } + } + }, + "EffectivePermission": "foo" + }, + "S3ObjectDetails": [ + { + "ObjectArn": "foo", + "Key": "foo", + "ETag": "foo", + "Hash": "foo", + "VersionId": "foo" + } + ] + } + ], + "InstanceDetails": { + "AvailabilityZone": "foo", + "IamInstanceProfile": { + "Arn": "foo", + "Id": "foo" + }, + "ImageDescription": "foo", + "ImageId": "foo", + "InstanceId": "foo", + "InstanceState": "foo", + "InstanceType": "foo", + "OutpostArn": "foo", + "LaunchTime": "foo", + "NetworkInterfaces": [ + { + "Ipv6Addresses": [ + "foo" + ], + "NetworkInterfaceId": "foo", + "PrivateDnsName": "foo", + "PrivateIpAddress": "foo", + "PrivateIpAddresses": [ + { + "PrivateDnsName": "foo", + "PrivateIpAddress": "foo" + } + ], + "PublicDnsName": "foo", + "PublicIp": "foo", + "SecurityGroups": [ + { + "GroupId": "foo", + "GroupName": "foo" + } + ], + "SubnetId": "foo", + "VpcId": "foo" + } + ], + "Platform": "foo", + "ProductCodes": [ + { + "Code": "foo", + "ProductType": "foo" + } + ], + "Tags": [ + { + "Key": "foo", + "Value": "foo" + } + ] + }, + "EksClusterDetails": { + "Name": "foo", + "Arn": "foo", + "VpcId": "foo", + "Status": "foo", + "Tags": [ + { + "Key": "foo", + "Value": "foo" + } + ], + "CreatedAt": "2024-10-17T11:10:43Z" + }, + "KubernetesDetails": { + "KubernetesUserDetails": { + "Username": "foo", + "Uid": "foo", + "Groups": [ + "foo" + ], + "SessionName": [ + "foo" + ], + "ImpersonatedUser": { + "Username": "foo", + "Groups": [ + "foo" + ] + } + }, + "KubernetesWorkloadDetails": { + "Name": "foo", + "Type": "foo", + "Uid": "foo", + "Namespace": "foo", + "HostNetwork": true, + "Containers": [ + { + "ContainerRuntime": "foo", + "Id": "foo", + "Name": "foo", + "Image": "foo", + "ImagePrefix": "foo", + "VolumeMounts": [ + { + "Name": "foo", + "MountPath": "foo" + } + ], + "SecurityContext": { + "Privileged": true, + "AllowPrivilegeEscalation": true + } + } + ], + "Volumes": [ + { + "Name": "foo", + "HostPath": { + "Path": "foo" + } + } + ], + "ServiceAccountName": "foo", + "HostIPC": true, + "HostPID": true + } + }, + "ResourceType": "foo", + "EbsVolumeDetails": { + "ScannedVolumeDetails": [ + { + "VolumeArn": "foo", + "VolumeType": "foo", + "DeviceName": "foo", + "VolumeSizeInGB": 123, + "EncryptionType": "foo", + "SnapshotArn": "foo", + "KmsKeyArn": "foo" + } + ], + "SkippedVolumeDetails": [ + { + "VolumeArn": "foo", + "VolumeType": "foo", + "DeviceName": "foo", + "VolumeSizeInGB": 123, + "EncryptionType": "foo", + "SnapshotArn": "foo", + "KmsKeyArn": "foo" + } + ] + }, + "EcsClusterDetails": { + "Name": "foo", + "Arn": "foo", + "Status": "foo", + "ActiveServicesCount": 123, + "RegisteredContainerInstancesCount": 123, + "RunningTasksCount": 123, + "Tags": [ + { + "Key": "foo", + "Value": "foo" + } + ], + "TaskDetails": { + "Arn": "foo", + "DefinitionArn": "foo", + "Version": "foo", + "TaskCreatedAt": "2024-10-17T11:10:43Z", + "StartedAt": "2024-10-17T11:10:43Z", + "StartedBy": "foo", + "Tags": [ + { + "Key": "foo", + "Value": "foo" + } + ], + "Volumes": [ + { + "Name": "foo", + "HostPath": { + "Path": "foo" + } + } + ], + "Containers": [ + { + "ContainerRuntime": "foo", + "Id": "foo", + "Name": "foo", + "Image": "foo", + "ImagePrefix": "foo", + "VolumeMounts": [ + { + "Name": "foo", + "MountPath": "foo" + } + ], + "SecurityContext": { + "Privileged": true, + "AllowPrivilegeEscalation": true + } + } + ], + "Group": "foo", + "LaunchType": "foo" + } + }, + "ContainerDetails": { + "ContainerRuntime": "foo", + "Id": "foo", + "Name": "foo", + "Image": "foo", + "ImagePrefix": "foo", + "VolumeMounts": [ + { + "Name": "foo", + "MountPath": "foo" + } + ], + "SecurityContext": { + "Privileged": true, + "AllowPrivilegeEscalation": true + } + }, + "RdsDbInstanceDetails": { + "DbInstanceIdentifier": "foo", + "Engine": "foo", + "EngineVersion": "foo", + "DbClusterIdentifier": "foo", + "DbInstanceArn": "foo", + "Tags": [ + { + "Key": "foo", + "Value": "foo" + } + ] + }, + "RdsDbUserDetails": { + "User": "foo", + "Application": "foo", + "Database": "foo", + "Ssl": "foo", + "AuthMethod": "foo" + }, + "LambdaDetails": { + "FunctionArn": "foo", + "FunctionName": "foo", + "Description": "foo", + "LastModifiedAt": "2024-10-17T11:10:43Z", + "RevisionId": "foo", + "FunctionVersion": "foo", + "Role": "foo", + "VpcConfig": { + "SubnetIds": [ + "foo" + ], + "VpcId": "foo", + "SecurityGroups": [ + { + "GroupId": "foo", + "GroupName": "foo" + } + ] + }, + "Tags": [ + { + "Key": "foo", + "Value": "foo" + } + ] + } + }, + "SchemaVersion": "foo", + "Service": { + "Action": { + "ActionType": "foo", + "AwsApiCallAction": { + "Api": "foo", + "CallerType": "foo", + "DomainDetails": { + "Domain": "foo" + }, + "ErrorCode": "foo", + "UserAgent": "foo", + "RemoteIpDetails": { + "City": { + "CityName": "foo" + }, + "Country": { + "CountryCode": "foo", + "CountryName": "foo" + }, + "GeoLocation": { + "Lat": 1.234, + "Lon": 1.234 + }, + "IpAddressV4": "foo", + "IpAddressV6": "foo", + "Organization": { + "Asn": "foo", + "AsnOrg": "foo", + "Isp": "foo", + "Org": "foo" + } + }, + "ServiceName": "foo", + "RemoteAccountDetails": { + "AccountId": "foo", + "Affiliated": true + }, + "AffectedResources": { + "0": "foo" + } + }, + "DnsRequestAction": { + "Domain": "foo", + "Protocol": "foo", + "Blocked": true, + "DomainWithSuffix": "foo" + }, + "NetworkConnectionAction": { + "Blocked": true, + "ConnectionDirection": "foo", + "LocalPortDetails": { + "Port": 123, + "PortName": "foo" + }, + "Protocol": "foo", + "LocalIpDetails": { + "IpAddressV4": "foo", + "IpAddressV6": "foo" + }, + "RemoteIpDetails": { + "City": { + "CityName": "foo" + }, + "Country": { + "CountryCode": "foo", + "CountryName": "foo" + }, + "GeoLocation": { + "Lat": 1.234, + "Lon": 1.234 + }, + "IpAddressV4": "foo", + "IpAddressV6": "foo", + "Organization": { + "Asn": "foo", + "AsnOrg": "foo", + "Isp": "foo", + "Org": "foo" + } + }, + "RemotePortDetails": { + "Port": 123, + "PortName": "foo" + } + }, + "PortProbeAction": { + "Blocked": true, + "PortProbeDetails": [ + { + "LocalPortDetails": { + "Port": 123, + "PortName": "foo" + }, + "LocalIpDetails": { + "IpAddressV4": "foo", + "IpAddressV6": "foo" + }, + "RemoteIpDetails": { + "City": { + "CityName": "foo" + }, + "Country": { + "CountryCode": "foo", + "CountryName": "foo" + }, + "GeoLocation": { + "Lat": 1.234, + "Lon": 1.234 + }, + "IpAddressV4": "foo", + "IpAddressV6": "foo", + "Organization": { + "Asn": "foo", + "AsnOrg": "foo", + "Isp": "foo", + "Org": "foo" + } + } + } + ] + }, + "KubernetesApiCallAction": { + "RequestUri": "foo", + "Verb": "foo", + "SourceIps": [ + "foo" + ], + "UserAgent": "foo", + "RemoteIpDetails": { + "City": { + "CityName": "foo" + }, + "Country": { + "CountryCode": "foo", + "CountryName": "foo" + }, + "GeoLocation": { + "Lat": 1.234, + "Lon": 1.234 + }, + "IpAddressV4": "foo", + "IpAddressV6": "foo", + "Organization": { + "Asn": "foo", + "AsnOrg": "foo", + "Isp": "foo", + "Org": "foo" + } + }, + "StatusCode": 123, + "Parameters": "foo", + "Resource": "foo", + "Subresource": "foo", + "Namespace": "foo", + "ResourceName": "foo" + }, + "RdsLoginAttemptAction": { + "RemoteIpDetails": { + "City": { + "CityName": "foo" + }, + "Country": { + "CountryCode": "foo", + "CountryName": "foo" + }, + "GeoLocation": { + "Lat": 1.234, + "Lon": 1.234 + }, + "IpAddressV4": "foo", + "IpAddressV6": "foo", + "Organization": { + "Asn": "foo", + "AsnOrg": "foo", + "Isp": "foo", + "Org": "foo" + } + }, + "LoginAttributes": [ + { + "User": "foo", + "Application": "foo", + "FailedLoginAttempts": 123, + "SuccessfulLoginAttempts": 123 + } + ] + }, + "KubernetesPermissionCheckedDetails": { + "Verb": "foo", + "Resource": "foo", + "Namespace": "foo", + "Allowed": true + }, + "KubernetesRoleBindingDetails": { + "Kind": "foo", + "Name": "foo", + "Uid": "foo", + "RoleRefName": "foo", + "RoleRefKind": "foo" + }, + "KubernetesRoleDetails": { + "Kind": "foo", + "Name": "foo", + "Uid": "foo" + } + }, + "Evidence": { + "ThreatIntelligenceDetails": [ + { + "ThreatListName": "foo", + "ThreatNames": [ + "foo" + ], + "ThreatFileSha256": "foo" + } + ] + }, + "Archived": true, + "Count": 123, + "DetectorId": "foo", + "EventFirstSeen": "foo", + "EventLastSeen": "foo", + "ResourceRole": "foo", + "ServiceName": "foo", + "UserFeedback": "foo", + "AdditionalInfo": { + "Value": "foo", + "Type": "foo" + }, + "FeatureName": "foo", + "EbsVolumeScanDetails": { + "ScanId": "foo", + "ScanStartedAt": "2024-10-17T11:10:43Z", + "ScanCompletedAt": "2024-10-17T11:10:43Z", + "TriggerFindingId": "foo", + "Sources": [ + "foo" + ], + "ScanDetections": { + "ScannedItemCount": { + "TotalGb": 123, + "Files": 123, + "Volumes": 123 + }, + "ThreatsDetectedItemCount": { + "Files": 123 + }, + "HighestSeverityThreatDetails": { + "Severity": "foo", + "ThreatName": "foo", + "Count": 123 + }, + "ThreatDetectedByName": { + "ItemCount": 123, + "UniqueThreatNameCount": 123, + "Shortened": true, + "ThreatNames": [ + { + "Name": "foo", + "Severity": "foo", + "ItemCount": 123, + "FilePaths": [ + { + "FilePath": "foo", + "VolumeArn": "foo", + "Hash": "foo", + "FileName": "foo" + } + ] + } + ] + } + }, + "ScanType": "ON_DEMAND" + }, + "RuntimeDetails": { + "Process": { + "Name": "foo", + "ExecutablePath": "foo", + "ExecutableSha256": "foo", + "NamespacePid": 123, + "Pwd": "foo", + "Pid": 123, + "StartTime": "2024-10-17T11:10:43Z", + "Uuid": "foo", + "ParentUuid": "foo", + "User": "foo", + "UserId": 123, + "Euid": 123, + "Lineage": [ + { + "StartTime": "2024-10-17T11:10:43Z", + "NamespacePid": 123, + "UserId": 123, + "Name": "foo", + "Pid": 123, + "Uuid": "foo", + "ExecutablePath": "foo", + "Euid": 123, + "ParentUuid": "foo" + } + ] + }, + "Context": { + "ModifyingProcess": { + "Name": "foo", + "ExecutablePath": "foo", + "ExecutableSha256": "foo", + "NamespacePid": 123, + "Pwd": "foo", + "Pid": 123, + "StartTime": "2024-10-17T11:10:43Z", + "Uuid": "foo", + "ParentUuid": "foo", + "User": "foo", + "UserId": 123, + "Euid": 123, + "Lineage": [ + { + "StartTime": "2024-10-17T11:10:43Z", + "NamespacePid": 123, + "UserId": 123, + "Name": "foo", + "Pid": 123, + "Uuid": "foo", + "ExecutablePath": "foo", + "Euid": 123, + "ParentUuid": "foo" + } + ] + }, + "ModifiedAt": "2024-10-17T11:10:43Z", + "ScriptPath": "foo", + "LibraryPath": "foo", + "LdPreloadValue": "foo", + "SocketPath": "foo", + "RuncBinaryPath": "foo", + "ReleaseAgentPath": "foo", + "MountSource": "foo", + "MountTarget": "foo", + "FileSystemType": "foo", + "Flags": [ + "foo" + ], + "ModuleName": "foo", + "ModuleFilePath": "foo", + "ModuleSha256": "foo", + "ShellHistoryFilePath": "foo", + "TargetProcess": { + "Name": "foo", + "ExecutablePath": "foo", + "ExecutableSha256": "foo", + "NamespacePid": 123, + "Pwd": "foo", + "Pid": 123, + "StartTime": "2024-10-17T11:10:43Z", + "Uuid": "foo", + "ParentUuid": "foo", + "User": "foo", + "UserId": 123, + "Euid": 123, + "Lineage": [ + { + "StartTime": "2024-10-17T11:10:43Z", + "NamespacePid": 123, + "UserId": 123, + "Name": "foo", + "Pid": 123, + "Uuid": "foo", + "ExecutablePath": "foo", + "Euid": 123, + "ParentUuid": "foo" + } + ] + }, + "AddressFamily": "foo", + "IanaProtocolNumber": 123, + "MemoryRegions": [ + "foo" + ], + "ToolName": "foo", + "ToolCategory": "foo", + "ServiceName": "foo", + "CommandLineExample": "foo", + "ThreatFilePath": "foo" + } + }, + "Detection": { + "Anomaly": { + "Profiles": { + "0": { + "0": [ + { + "ProfileType": "FREQUENCY", + "ProfileSubtype": "RARE", + "Observations": { + "Text": [ + "foo" + ] + } + } + ] + } + }, + "Unusual": { + "Behavior": { + "0": { + "0": { + "ProfileType": "FREQUENCY", + "ProfileSubtype": "RARE", + "Observations": { + "Text": [ + "foo" + ] + } + } + } + } + } + } + }, + "MalwareScanDetails": { + "Threats": [ + { + "Name": "foo", + "Source": "foo", + "ItemPaths": [ + { + "NestedItemPath": "foo", + "Hash": "foo" + } + ] + } + ] + } + }, + "Severity": 1.234, + "Title": "foo", + "Type": "foo", + "UpdatedAt": "foo" + } + ] +} \ No newline at end of file diff --git a/plugins/aws/test/resources/files/guardduty/list-findings__foo.json b/plugins/aws/test/resources/files/guardduty/list-findings__foo.json index 356f170ca5..0a3f8a06b8 100644 --- a/plugins/aws/test/resources/files/guardduty/list-findings__foo.json +++ b/plugins/aws/test/resources/files/guardduty/list-findings__foo.json @@ -1,7 +1,5 @@ { "FindingIds": [ - "foo", - "foo", "foo" ], "NextToken": "foo" diff --git a/plugins/aws/test/resources/guardduty.py b/plugins/aws/test/resources/guardduty.py new file mode 100644 index 0000000000..69a95b4db5 --- /dev/null +++ b/plugins/aws/test/resources/guardduty.py @@ -0,0 +1,7 @@ +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding +from test.resources import round_trip_for + + +def test_notebooks() -> None: + _, builder = round_trip_for(AwsGuardDutyFinding) + assert len(builder.resources_of(AwsGuardDutyFinding)) == 1 diff --git a/plugins/aws/tools/aws_model_gen.py b/plugins/aws/tools/aws_model_gen.py index fedc4c670c..f6ab0aef08 100644 --- a/plugins/aws/tools/aws_model_gen.py +++ b/plugins/aws/tools/aws_model_gen.py @@ -291,10 +291,10 @@ def sample(shape: Shape) -> JsonElement: return "foo" elif isinstance(shape, ListShape): inner = shape.member - return [sample(inner) for _ in range(3)] + return [sample(inner)] elif isinstance(shape, MapShape): value_type = shape.value - return {f"{num}": sample(value_type) for num in range(3)} + return {f"{num}": sample(value_type) for num in range(1)} elif isinstance(shape, StructureShape): return {name: sample(shape) for name, shape in shape.members.items()} elif shape.type_name == "double": From e6541b739c80d136c6ab76761d508c998ea80c6c Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 17 Oct 2024 11:33:10 +0000 Subject: [PATCH 04/18] feat: added resource metadata --- .../aws/fix_plugin_aws/resource/guardduty.py | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 86a4674dcd..65f8252355 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -12,7 +12,7 @@ from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance from fix_plugin_aws.resource.s3 import AwsS3Bucket -from fixlib.baseresources import PhantomBaseResource +from fixlib.baseresources import ModelReference, PhantomBaseResource from fixlib.json_bender import F, S, AsInt, Bend, Bender, ForallBend from fixlib.types import Json from fixlib.utils import chunks, utc_str @@ -1214,9 +1214,36 @@ class AwsGuardDutyService: @define(eq=False, slots=False) -class AwsGuardDutyFinding(AwsResource): +class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): kind: ClassVar[str] = "aws_guard_duty_finding" - # Collected via AwsGuardDutyDetector() + _kind_display: ClassVar[str] = "AWS GuardDuty Finding" + _kind_description: ClassVar[str] = ( + "AWS GuardDuty Finding represents a potential security issue identified by Amazon GuardDuty. " + "GuardDuty uses machine learning, anomaly detection, and integrated threat intelligence to detect and " + "alert on suspicious activity in your AWS environment. Findings highlight possible attacks or vulnerabilities " + "that may require further investigation." + ) + _kind_service: ClassVar[Optional[str]] = service_name + _metadata: ClassVar[Dict[str, Any]] = {"icon": "log", "group": "management"} + _docs_url: ClassVar[str] = "https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html" + _reference_kinds: ClassVar[ModelReference] = { + "successors": { + "default": [ + AwsS3Bucket.kind, + AwsEc2Instance.kind, + AwsEksCluster.kind, + AwsEc2Volume.kind, + AwsEcsCluster.kind, + AwsRdsInstance.kind, + AwsRdsCluster.kind, + AwsLambdaFunction.kind, + ] + } + } + _aws_metadata: ClassVar[Dict[str, Any]] = { + "provider_link_tpl": "https://{region_id}.console.aws.amazon.com/guardduty/home?region={region}#/findings?fId={id}¯os=current", + } + # api spec defined in `collect_resources` mapping: ClassVar[Dict[str, Bender]] = { "id": S("Id"), "name": S("Title"), @@ -1247,6 +1274,10 @@ class AwsGuardDutyFinding(AwsResource): title: Optional[str] = field(default=None, metadata={"description": "The title of the finding."}) # fmt: skip type: Optional[str] = field(default=None, metadata={"description": "The type of finding."}) # fmt: skip + @classmethod + def service_name(cls) -> str: + return service_name + @classmethod def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: try: From 6251e1858f2e66957e3c461b921831cd06c05d2b Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 17 Oct 2024 11:35:37 +0000 Subject: [PATCH 05/18] chore: comment model gen --- plugins/aws/tools/aws_model_gen.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/aws/tools/aws_model_gen.py b/plugins/aws/tools/aws_model_gen.py index f6ab0aef08..4f882e25aa 100644 --- a/plugins/aws/tools/aws_model_gen.py +++ b/plugins/aws/tools/aws_model_gen.py @@ -986,12 +986,12 @@ def default_imports() -> str: # ) ], "guardduty": [ - AwsFixModel( - api_action="get-findings", - result_property="Findings", - result_shape="GetFindingsResponse", - prefix="GuardDuty", - ), + # AwsFixModel( + # api_action="get-findings", + # result_property="Findings", + # result_shape="GetFindingsResponse", + # prefix="GuardDuty", + # ), ], "inspector2": [ # AwsFixModel( From 10642901a7dff2f564e81a031a7ad3ec0d96b2c9 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 17 Oct 2024 11:47:45 +0000 Subject: [PATCH 06/18] fixed provider link --- plugins/aws/fix_plugin_aws/resource/guardduty.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 65f8252355..034a705b70 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -1241,7 +1241,7 @@ class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): } } _aws_metadata: ClassVar[Dict[str, Any]] = { - "provider_link_tpl": "https://{region_id}.console.aws.amazon.com/guardduty/home?region={region}#/findings?fId={id}¯os=current", + "provider_link_tpl": "https://{region_id}.console.aws.amazon.com/guardduty/home?region={region_id}#/findings?fId={id}¯os=current", } # api spec defined in `collect_resources` mapping: ClassVar[Dict[str, Bender]] = { From ce57d40a4a33ec193353b35469228741e74a6d5a Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 21 Oct 2024 11:28:31 +0000 Subject: [PATCH 07/18] feat: added new features --- plugins/aws/fix_plugin_aws/resource/base.py | 10 + .../aws/fix_plugin_aws/resource/guardduty.py | 246 ++++++++++++------ 2 files changed, 179 insertions(+), 77 deletions(-) diff --git a/plugins/aws/fix_plugin_aws/resource/base.py b/plugins/aws/fix_plugin_aws/resource/base.py index 75a7158f73..0d74b259bb 100644 --- a/plugins/aws/fix_plugin_aws/resource/base.py +++ b/plugins/aws/fix_plugin_aws/resource/base.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections import defaultdict import logging import re from abc import ABC @@ -27,6 +28,7 @@ BaseVolumeType, Cloud, EdgeType, + Finding, ModelReference, PhantomBaseResource, BaseOrganizationalRoot, @@ -63,6 +65,11 @@ def get_client(config: Config, resource: BaseResource) -> AwsClient: "region_id": lambda n: n.region().id, } +# Type alias for a tuple representing provider, region, and resource type +AssessmentKey = Tuple[str, str, str] +# Type alias for the inner dictionary that maps resource ID to a list of findings +ResourceFindings = Dict[str, List[Finding]] + def parse_json( json: Json, clazz: Type[T], builder: GraphBuilder, mapping: Optional[Dict[str, Bender]] = None @@ -501,6 +508,9 @@ def __init__( def suppress(self, message: str) -> SuppressWithFeedback: return SuppressWithFeedback(message, self.core_feedback, log) + def add_finding(self, provider: str, class_name: str, region: str, class_id: str, finding: Finding) -> None: + self._assessment_findings[(provider, region, class_name)][class_id].append(finding) + def submit_work(self, service: str, fn: Callable[..., T], *args: Any, **kwargs: Any) -> Future[T]: """ Use this method for work that can be done in parallel. diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 034a705b70..112701d192 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -1,18 +1,19 @@ from datetime import datetime, timezone -from typing import ClassVar, Dict, List, Optional, Type, Any +from typing import ClassVar, Dict, List, Optional, Tuple, Type, Any import logging from attrs import define, field from boto3.exceptions import Boto3Error from fix_plugin_aws.resource.base import AwsResource, GraphBuilder -from fix_plugin_aws.resource.ec2 import AwsEc2Instance, AwsEc2Volume -from fix_plugin_aws.resource.ecs import AwsEcsCluster -from fix_plugin_aws.resource.eks import AwsEksCluster -from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction -from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance -from fix_plugin_aws.resource.s3 import AwsS3Bucket -from fixlib.baseresources import ModelReference, PhantomBaseResource + +# from fix_plugin_aws.resource.ec2 import AwsEc2Instance, AwsEc2Volume +# from fix_plugin_aws.resource.ecs import AwsEcsCluster +# from fix_plugin_aws.resource.eks import AwsEksCluster +# from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction +# from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance +# from fix_plugin_aws.resource.s3 import AwsS3Bucket +from fixlib.baseresources import PhantomBaseResource from fixlib.json_bender import F, S, AsInt, Bend, Bender, ForallBend from fixlib.types import Json from fixlib.utils import chunks, utc_str @@ -1226,20 +1227,20 @@ class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): _kind_service: ClassVar[Optional[str]] = service_name _metadata: ClassVar[Dict[str, Any]] = {"icon": "log", "group": "management"} _docs_url: ClassVar[str] = "https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html" - _reference_kinds: ClassVar[ModelReference] = { - "successors": { - "default": [ - AwsS3Bucket.kind, - AwsEc2Instance.kind, - AwsEksCluster.kind, - AwsEc2Volume.kind, - AwsEcsCluster.kind, - AwsRdsInstance.kind, - AwsRdsCluster.kind, - AwsLambdaFunction.kind, - ] - } - } + # _reference_kinds: ClassVar[ModelReference] = { + # "successors": { + # "default": [ + # AwsS3Bucket.kind, + # AwsEc2Instance.kind, + # AwsEksCluster.kind, + # AwsEc2Volume.kind, + # AwsEcsCluster.kind, + # AwsRdsInstance.kind, + # AwsRdsCluster.kind, + # AwsLambdaFunction.kind, + # ] + # } + # } _aws_metadata: ClassVar[Dict[str, Any]] = { "provider_link_tpl": "https://{region_id}.console.aws.amazon.com/guardduty/home?region={region_id}#/findings?fId={id}¯os=current", } @@ -1278,8 +1279,88 @@ class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): def service_name(cls) -> str: return service_name + def parse_finding(self, source: Json) -> Finding: + SEVERITY_MAPPING = { + 1: "INFORMATIONAL", # Minimal impact + 2: "INFORMATIONAL", # Minor impact + 3: "LOW", # Notable but manageable + 4: "LOW", # Moderate + 5: "MEDIUM", # Considerable + 6: "MEDIUM", # Serious + 7: "HIGH", # Severe impact + 8: "HIGH", # Critical impact + 9: "CRITICAL", # Extremely critical + 10: "CRITICAL", # Catastrophic + } + severity_map = { + "INFORMATIONAL": Severity.info, + "LOW": Severity.low, + "MEDIUM": Severity.medium, + "HIGH": Severity.high, + "CRITICAL": Severity.critical, + } + finding_title = self.safe_name + if not self.finding_severity: + finding_severity = Severity.medium + else: + finding_severity = severity_mapping.get(self.finding_severity, Severity.medium) + description = self.description + remidiation = "" + if self.remediation and self.remediation.recommendation: + remidiation = self.remediation.recommendation.text or "" + updated_at = self.updated_at + details = source.get("packageVulnerabilityDetails", {}) | source.get("codeVulnerabilityDetails", {}) + return Finding(finding_title, finding_severity, description, remidiation, updated_at, details) + @classmethod def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: + + def check_type_and_adjust_id( + finding_resource: AwsGuardDutyResource, + ) -> Optional[Tuple[str, str]]: + # To avoid circular imports, defined here + from fix_plugin_aws.resource.ec2 import AwsEc2Instance, AwsEc2Volume + from fix_plugin_aws.resource.ecs import AwsEcsCluster + from fix_plugin_aws.resource.eks import AwsEksCluster + from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction + from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance + from fix_plugin_aws.resource.s3 import AwsS3Bucket + + # Check and return the first found resource type and its corresponding information + if finding_resource.s3_bucket_details: + for s3_bucket_detail in finding_resource.s3_bucket_details: + if s3_bucket_detail.name: + return (AwsS3Bucket.__name__, s3_bucket_detail.name) + + if finding_resource.instance_details and finding_resource.instance_details.instance_id: + return (AwsEc2Instance.__name__, finding_resource.instance_details.instance_id) + + if finding_resource.eks_cluster_details and finding_resource.eks_cluster_details.arn: + return (AwsEksCluster.__name__, finding_resource.eks_cluster_details.arn) + + if finding_resource.ebs_volume_details: + for vol_detail in finding_resource.ebs_volume_details.scanned_volume_details or []: + if vol_detail.volume_arn: + return (AwsEc2Volume.__name__, vol_detail.volume_arn) + + for vol_detail in finding_resource.ebs_volume_details.skipped_volume_details or []: + if vol_detail.volume_arn: + return (AwsEc2Volume.__name__, vol_detail.volume_arn) + + if finding_resource.ecs_cluster_details and finding_resource.ecs_cluster_details.arn: + return (AwsEcsCluster.__name__, finding_resource.ecs_cluster_details.arn) + + if finding_resource.rds_db_instance_details: + if finding_resource.rds_db_instance_details.db_instance_arn: + return (AwsRdsInstance.__name__, finding_resource.rds_db_instance_details.db_instance_arn) + if finding_resource.rds_db_instance_details.db_cluster_identifier: + return (AwsRdsCluster.__name__, finding_resource.rds_db_instance_details.db_cluster_identifier) + + if finding_resource.lambda_details and finding_resource.lambda_details.function_name: + return (AwsLambdaFunction.__name__, finding_resource.lambda_details.function_name) + + return None + try: detector_ids = builder.client.list( service_name, @@ -1305,7 +1386,18 @@ def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: ): if finding.get("AccountId", None) == builder.account.id: if instance := AwsGuardDutyFinding.from_api(finding, builder): - builder.add_node(instance, finding) + if fr := instance.finding_resource: + found_info = check_type_and_adjust_id(fr) + if found_info: + class_name, id_or_arn_or_name = found_info + adjusted_finding = instance.parse_finding(finding) + builder.add_finding( + "guard_duty", + class_name, + instance.finding_region or "global", + id_or_arn_or_name, + adjusted_finding, + ) except Boto3Error as e: msg = f"Error while collecting {cls.__name__} in region {builder.region.name}: {e}" builder.core_feedback.error(msg, log) @@ -1315,60 +1407,60 @@ def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: builder.core_feedback.info(msg, log) raise - def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: - resource = self.finding_resource - if resource: - for s3_bucket_detail in resource.s3_bucket_details or []: - if s3_bucket_detail.name: - builder.add_edge(self, clazz=AwsS3Bucket, name=s3_bucket_detail.name) - if resource.instance_details and resource.instance_details.instance_id: - builder.add_edge( - self, - clazz=AwsEc2Instance, - id=resource.instance_details.instance_id, - ) - if resource.eks_cluster_details and resource.eks_cluster_details.arn: - builder.add_edge( - self, - clazz=AwsEksCluster, - arn=resource.eks_cluster_details.arn, - ) - - if resource.ebs_volume_details: - for vol_detail in resource.ebs_volume_details.scanned_volume_details or []: - if vol_detail.volume_arn: - builder.add_edge(self, clazz=AwsEc2Volume, arn=vol_detail.volume_arn) - for vol_detail in resource.ebs_volume_details.skipped_volume_details or []: - if vol_detail.volume_arn: - builder.add_edge(self, clazz=AwsEc2Volume, arn=vol_detail.volume_arn) - - if resource.ecs_cluster_details and resource.ecs_cluster_details.arn: - builder.add_edge( - self, - clazz=AwsEcsCluster, - arn=resource.ecs_cluster_details.arn, - ) - - if resource.rds_db_instance_details: - if resource.rds_db_instance_details.db_instance_arn: - builder.add_edge( - self, - clazz=AwsRdsInstance, - arn=resource.rds_db_instance_details.db_instance_arn, - ) - if resource.rds_db_instance_details.db_cluster_identifier: - builder.add_edge( - self, - clazz=AwsRdsCluster, - id=resource.rds_db_instance_details.db_cluster_identifier, - ) - - if resource.lambda_details and resource.lambda_details.function_name: - builder.add_edge( - self, - clazz=AwsLambdaFunction, - name=resource.lambda_details.function_name, - ) + # def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: + # resource = self.finding_resource + # if resource: + # for s3_bucket_detail in resource.s3_bucket_details or []: + # if s3_bucket_detail.name: + # builder.add_edge(self, clazz=AwsS3Bucket, name=s3_bucket_detail.name) + # if resource.instance_details and resource.instance_details.instance_id: + # builder.add_edge( + # self, + # clazz=AwsEc2Instance, + # id=resource.instance_details.instance_id, + # ) + # if resource.eks_cluster_details and resource.eks_cluster_details.arn: + # builder.add_edge( + # self, + # clazz=AwsEksCluster, + # arn=resource.eks_cluster_details.arn, + # ) + + # if resource.ebs_volume_details: + # for vol_detail in resource.ebs_volume_details.scanned_volume_details or []: + # if vol_detail.volume_arn: + # builder.add_edge(self, clazz=AwsEc2Volume, arn=vol_detail.volume_arn) + # for vol_detail in resource.ebs_volume_details.skipped_volume_details or []: + # if vol_detail.volume_arn: + # builder.add_edge(self, clazz=AwsEc2Volume, arn=vol_detail.volume_arn) + + # if resource.ecs_cluster_details and resource.ecs_cluster_details.arn: + # builder.add_edge( + # self, + # clazz=AwsEcsCluster, + # arn=resource.ecs_cluster_details.arn, + # ) + + # if resource.rds_db_instance_details: + # if resource.rds_db_instance_details.db_instance_arn: + # builder.add_edge( + # self, + # clazz=AwsRdsInstance, + # arn=resource.rds_db_instance_details.db_instance_arn, + # ) + # if resource.rds_db_instance_details.db_cluster_identifier: + # builder.add_edge( + # self, + # clazz=AwsRdsCluster, + # id=resource.rds_db_instance_details.db_cluster_identifier, + # ) + + # if resource.lambda_details and resource.lambda_details.function_name: + # builder.add_edge( + # self, + # clazz=AwsLambdaFunction, + # name=resource.lambda_details.function_name, + # ) resources: List[Type[AwsResource]] = [AwsGuardDutyFinding] From 7e1a1fdf70cc55acd0195545c56a791243e006af Mon Sep 17 00:00:00 2001 From: Kirill Date: Mon, 21 Oct 2024 11:49:36 +0000 Subject: [PATCH 08/18] feat: added set_findings method --- .../aws/fix_plugin_aws/resource/guardduty.py | 141 ++++++------------ 1 file changed, 46 insertions(+), 95 deletions(-) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 112701d192..3ac9eff3ef 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -7,13 +7,7 @@ from fix_plugin_aws.resource.base import AwsResource, GraphBuilder -# from fix_plugin_aws.resource.ec2 import AwsEc2Instance, AwsEc2Volume -# from fix_plugin_aws.resource.ecs import AwsEcsCluster -# from fix_plugin_aws.resource.eks import AwsEksCluster -# from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction -# from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance -# from fix_plugin_aws.resource.s3 import AwsS3Bucket -from fixlib.baseresources import PhantomBaseResource +from fixlib.baseresources import Assessment, Finding, PhantomBaseResource, Severity from fixlib.json_bender import F, S, AsInt, Bend, Bender, ForallBend from fixlib.types import Json from fixlib.utils import chunks, utc_str @@ -1227,20 +1221,6 @@ class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): _kind_service: ClassVar[Optional[str]] = service_name _metadata: ClassVar[Dict[str, Any]] = {"icon": "log", "group": "management"} _docs_url: ClassVar[str] = "https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html" - # _reference_kinds: ClassVar[ModelReference] = { - # "successors": { - # "default": [ - # AwsS3Bucket.kind, - # AwsEc2Instance.kind, - # AwsEksCluster.kind, - # AwsEc2Volume.kind, - # AwsEcsCluster.kind, - # AwsRdsInstance.kind, - # AwsRdsCluster.kind, - # AwsLambdaFunction.kind, - # ] - # } - # } _aws_metadata: ClassVar[Dict[str, Any]] = { "provider_link_tpl": "https://{region_id}.console.aws.amazon.com/guardduty/home?region={region_id}#/findings?fId={id}¯os=current", } @@ -1279,19 +1259,48 @@ class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): def service_name(cls) -> str: return service_name + @staticmethod + def set_findings(builder: GraphBuilder, resource_to_set: AwsResource, to_check: str = "id") -> None: + """ + Set the assessment findings for the resource based on its ID or ARN. + """ + if not isinstance(resource_to_set, AwsResource): + return + + id_or_arn = "" + + if to_check == "arn": + if not resource_to_set.arn: + return + id_or_arn = resource_to_set.arn + elif to_check == "id": + id_or_arn = resource_to_set.id + elif to_check == "name": + id_or_arn = resource_to_set.safe_name + else: + return + provider_findings = builder._assessment_findings.get( + ("guard_duty", resource_to_set.region().id, resource_to_set.__class__.__name__), {} + ).get(id_or_arn, []) + if provider_findings: + # Set the findings in the resource's _assessments dictionary + resource_to_set._assessments.append(Assessment("guard_duty", provider_findings)) + def parse_finding(self, source: Json) -> Finding: - SEVERITY_MAPPING = { - 1: "INFORMATIONAL", # Minimal impact - 2: "INFORMATIONAL", # Minor impact - 3: "LOW", # Notable but manageable - 4: "LOW", # Moderate - 5: "MEDIUM", # Considerable - 6: "MEDIUM", # Serious - 7: "HIGH", # Severe impact - 8: "HIGH", # Critical impact - 9: "CRITICAL", # Extremely critical - 10: "CRITICAL", # Catastrophic - } + def get_severity() -> str: + if not self.finding_severity: + return "MEDIUM" + if self.finding_severity <= 2: + return "INFORMATIONAL" + elif self.finding_severity <= 4: + return "LOW" + elif self.finding_severity <= 6: + return "MEDIUM" + elif self.finding_severity <= 8: + return "HIGH" + else: + return "CRITICAL" + severity_map = { "INFORMATIONAL": Severity.info, "LOW": Severity.low, @@ -1303,14 +1312,11 @@ def parse_finding(self, source: Json) -> Finding: if not self.finding_severity: finding_severity = Severity.medium else: - finding_severity = severity_mapping.get(self.finding_severity, Severity.medium) + finding_severity = severity_map.get(get_severity(), Severity.medium) description = self.description - remidiation = "" - if self.remediation and self.remediation.recommendation: - remidiation = self.remediation.recommendation.text or "" - updated_at = self.updated_at - details = source.get("packageVulnerabilityDetails", {}) | source.get("codeVulnerabilityDetails", {}) - return Finding(finding_title, finding_severity, description, remidiation, updated_at, details) + updated_at = self.mtime + details = source.get("Service", {}) + return Finding(finding_title, finding_severity, description, None, updated_at, details) @classmethod def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: @@ -1407,60 +1413,5 @@ def check_type_and_adjust_id( builder.core_feedback.info(msg, log) raise - # def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: - # resource = self.finding_resource - # if resource: - # for s3_bucket_detail in resource.s3_bucket_details or []: - # if s3_bucket_detail.name: - # builder.add_edge(self, clazz=AwsS3Bucket, name=s3_bucket_detail.name) - # if resource.instance_details and resource.instance_details.instance_id: - # builder.add_edge( - # self, - # clazz=AwsEc2Instance, - # id=resource.instance_details.instance_id, - # ) - # if resource.eks_cluster_details and resource.eks_cluster_details.arn: - # builder.add_edge( - # self, - # clazz=AwsEksCluster, - # arn=resource.eks_cluster_details.arn, - # ) - - # if resource.ebs_volume_details: - # for vol_detail in resource.ebs_volume_details.scanned_volume_details or []: - # if vol_detail.volume_arn: - # builder.add_edge(self, clazz=AwsEc2Volume, arn=vol_detail.volume_arn) - # for vol_detail in resource.ebs_volume_details.skipped_volume_details or []: - # if vol_detail.volume_arn: - # builder.add_edge(self, clazz=AwsEc2Volume, arn=vol_detail.volume_arn) - - # if resource.ecs_cluster_details and resource.ecs_cluster_details.arn: - # builder.add_edge( - # self, - # clazz=AwsEcsCluster, - # arn=resource.ecs_cluster_details.arn, - # ) - - # if resource.rds_db_instance_details: - # if resource.rds_db_instance_details.db_instance_arn: - # builder.add_edge( - # self, - # clazz=AwsRdsInstance, - # arn=resource.rds_db_instance_details.db_instance_arn, - # ) - # if resource.rds_db_instance_details.db_cluster_identifier: - # builder.add_edge( - # self, - # clazz=AwsRdsCluster, - # id=resource.rds_db_instance_details.db_cluster_identifier, - # ) - - # if resource.lambda_details and resource.lambda_details.function_name: - # builder.add_edge( - # self, - # clazz=AwsLambdaFunction, - # name=resource.lambda_details.function_name, - # ) - resources: List[Type[AwsResource]] = [AwsGuardDutyFinding] From ec8f0a1719930d4f5a966f4e2cd6be0c97787921 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 22 Oct 2024 09:58:49 +0000 Subject: [PATCH 09/18] feat: added setting findings to the resources --- plugins/aws/fix_plugin_aws/resource/ec2.py | 4 ++ plugins/aws/fix_plugin_aws/resource/ecs.py | 2 + plugins/aws/fix_plugin_aws/resource/eks.py | 2 + .../aws/fix_plugin_aws/resource/guardduty.py | 46 +++++++++---------- .../aws/fix_plugin_aws/resource/lambda_.py | 2 + plugins/aws/fix_plugin_aws/resource/rds.py | 4 ++ plugins/aws/fix_plugin_aws/resource/s3.py | 5 ++ 7 files changed, 42 insertions(+), 23 deletions(-) diff --git a/plugins/aws/fix_plugin_aws/resource/ec2.py b/plugins/aws/fix_plugin_aws/resource/ec2.py index 881b234308..4309253a39 100644 --- a/plugins/aws/fix_plugin_aws/resource/ec2.py +++ b/plugins/aws/fix_plugin_aws/resource/ec2.py @@ -18,6 +18,7 @@ operations_to_iops, normalizer_factory, ) +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.iam import AwsIamInstanceProfile from fix_plugin_aws.resource.kms import AwsKmsKey from fix_plugin_aws.resource.s3 import AwsS3Bucket @@ -705,6 +706,7 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: clazz=AwsKmsKey, id=AwsKmsKey.normalise_id(self.volume_kms_key_id), ) + AwsGuardDutyFinding.set_findings(builder, self, "arn") def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call( @@ -1522,6 +1524,8 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: if iam_profile := self.instance_iam_instance_profile: builder.add_edge(self, reverse=True, clazz=AwsIamInstanceProfile, arn=iam_profile.arn) + AwsGuardDutyFinding.set_findings(builder, self, "id") + def delete_resource(self, client: AwsClient, graph: Graph) -> bool: if self.instance_status == InstanceStatus.TERMINATED: self.log("Instance is already terminated") diff --git a/plugins/aws/fix_plugin_aws/resource/ecs.py b/plugins/aws/fix_plugin_aws/resource/ecs.py index 7570e20cb3..9d52e0c5e1 100644 --- a/plugins/aws/fix_plugin_aws/resource/ecs.py +++ b/plugins/aws/fix_plugin_aws/resource/ecs.py @@ -6,6 +6,7 @@ from attrs import define, field from fix_plugin_aws.aws_client import AwsClient from fix_plugin_aws.resource.autoscaling import AwsAutoScalingGroup +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.utils import TagsValue, ToDict from fix_plugin_aws.resource.base import AwsResource, GraphBuilder, AwsApiSpec @@ -2110,6 +2111,7 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: builder.dependant_node(self, clazz=AwsKmsKey, id=AwsKmsKey.normalise_id(exc.kms_key_id)) if exc.log_configuration and exc.log_configuration.s3_bucket_name: builder.add_edge(self, clazz=AwsS3Bucket, name=exc.log_configuration.s3_bucket_name) + AwsGuardDutyFinding.set_findings(builder, self, "arn") def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call(aws_service=self.api_spec.service, action="delete-cluster", result_name=None, cluster=self.arn) diff --git a/plugins/aws/fix_plugin_aws/resource/eks.py b/plugins/aws/fix_plugin_aws/resource/eks.py index 9ad54520b6..a5ea970e9f 100644 --- a/plugins/aws/fix_plugin_aws/resource/eks.py +++ b/plugins/aws/fix_plugin_aws/resource/eks.py @@ -6,6 +6,7 @@ from fix_plugin_aws.resource.autoscaling import AwsAutoScalingGroup from fix_plugin_aws.resource.base import AwsResource, GraphBuilder, AwsApiSpec +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.iam import AwsIamRole from fix_plugin_aws.aws_client import AwsClient from fixlib.baseresources import ModelReference, BaseManagedKubernetesClusterProvider @@ -477,6 +478,7 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: builder.dependant_node( self, reverse=True, delete_same_as_default=True, clazz=AwsIamRole, arn=self.cluster_role_arn ) + AwsGuardDutyFinding.set_findings(builder, self, "arn") def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call(aws_service=self.api_spec.service, action="delete-cluster", result_name=None, name=self.name) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 3ac9eff3ef..d7563265c3 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -1264,24 +1264,21 @@ def set_findings(builder: GraphBuilder, resource_to_set: AwsResource, to_check: """ Set the assessment findings for the resource based on its ID or ARN. """ - if not isinstance(resource_to_set, AwsResource): - return - - id_or_arn = "" + id_or_arn_or_name = "" if to_check == "arn": if not resource_to_set.arn: return - id_or_arn = resource_to_set.arn + id_or_arn_or_name = resource_to_set.arn elif to_check == "id": - id_or_arn = resource_to_set.id + id_or_arn_or_name = resource_to_set.id elif to_check == "name": - id_or_arn = resource_to_set.safe_name + id_or_arn_or_name = resource_to_set.safe_name else: return provider_findings = builder._assessment_findings.get( ("guard_duty", resource_to_set.region().id, resource_to_set.__class__.__name__), {} - ).get(id_or_arn, []) + ).get(id_or_arn_or_name, []) if provider_findings: # Set the findings in the resource's _assessments dictionary resource_to_set._assessments.append(Assessment("guard_duty", provider_findings)) @@ -1323,7 +1320,7 @@ def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: def check_type_and_adjust_id( finding_resource: AwsGuardDutyResource, - ) -> Optional[Tuple[str, str]]: + ) -> List[Tuple[str, str]]: # To avoid circular imports, defined here from fix_plugin_aws.resource.ec2 import AwsEc2Instance, AwsEc2Volume from fix_plugin_aws.resource.ecs import AwsEcsCluster @@ -1332,40 +1329,44 @@ def check_type_and_adjust_id( from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance from fix_plugin_aws.resource.s3 import AwsS3Bucket - # Check and return the first found resource type and its corresponding information + finding_resources = [] if finding_resource.s3_bucket_details: for s3_bucket_detail in finding_resource.s3_bucket_details: if s3_bucket_detail.name: - return (AwsS3Bucket.__name__, s3_bucket_detail.name) + finding_resources.append((AwsS3Bucket.__name__, s3_bucket_detail.name)) if finding_resource.instance_details and finding_resource.instance_details.instance_id: - return (AwsEc2Instance.__name__, finding_resource.instance_details.instance_id) + finding_resources.append((AwsEc2Instance.__name__, finding_resource.instance_details.instance_id)) if finding_resource.eks_cluster_details and finding_resource.eks_cluster_details.arn: - return (AwsEksCluster.__name__, finding_resource.eks_cluster_details.arn) + finding_resources.append((AwsEksCluster.__name__, finding_resource.eks_cluster_details.arn)) if finding_resource.ebs_volume_details: for vol_detail in finding_resource.ebs_volume_details.scanned_volume_details or []: if vol_detail.volume_arn: - return (AwsEc2Volume.__name__, vol_detail.volume_arn) + finding_resources.append((AwsEc2Volume.__name__, vol_detail.volume_arn)) for vol_detail in finding_resource.ebs_volume_details.skipped_volume_details or []: if vol_detail.volume_arn: - return (AwsEc2Volume.__name__, vol_detail.volume_arn) + finding_resources.append((AwsEc2Volume.__name__, vol_detail.volume_arn)) if finding_resource.ecs_cluster_details and finding_resource.ecs_cluster_details.arn: - return (AwsEcsCluster.__name__, finding_resource.ecs_cluster_details.arn) + finding_resources.append((AwsEcsCluster.__name__, finding_resource.ecs_cluster_details.arn)) if finding_resource.rds_db_instance_details: - if finding_resource.rds_db_instance_details.db_instance_arn: - return (AwsRdsInstance.__name__, finding_resource.rds_db_instance_details.db_instance_arn) + if finding_resource.rds_db_instance_details.db_instance_identifier: + finding_resources.append( + (AwsRdsInstance.__name__, finding_resource.rds_db_instance_details.db_instance_identifier) + ) if finding_resource.rds_db_instance_details.db_cluster_identifier: - return (AwsRdsCluster.__name__, finding_resource.rds_db_instance_details.db_cluster_identifier) + finding_resources.append( + (AwsRdsCluster.__name__, finding_resource.rds_db_instance_details.db_cluster_identifier) + ) if finding_resource.lambda_details and finding_resource.lambda_details.function_name: - return (AwsLambdaFunction.__name__, finding_resource.lambda_details.function_name) + finding_resources.append((AwsLambdaFunction.__name__, finding_resource.lambda_details.function_name)) - return None + return finding_resources try: detector_ids = builder.client.list( @@ -1394,8 +1395,7 @@ def check_type_and_adjust_id( if instance := AwsGuardDutyFinding.from_api(finding, builder): if fr := instance.finding_resource: found_info = check_type_and_adjust_id(fr) - if found_info: - class_name, id_or_arn_or_name = found_info + for class_name, id_or_arn_or_name in found_info: adjusted_finding = instance.parse_finding(finding) builder.add_finding( "guard_duty", diff --git a/plugins/aws/fix_plugin_aws/resource/lambda_.py b/plugins/aws/fix_plugin_aws/resource/lambda_.py index 599bef09ce..c435615942 100644 --- a/plugins/aws/fix_plugin_aws/resource/lambda_.py +++ b/plugins/aws/fix_plugin_aws/resource/lambda_.py @@ -10,6 +10,7 @@ from fix_plugin_aws.resource.base import AwsResource, GraphBuilder, AwsApiSpec, parse_json from fix_plugin_aws.resource.cloudwatch import AwsCloudwatchQuery, normalizer_factory from fix_plugin_aws.resource.ec2 import AwsEc2Subnet, AwsEc2SecurityGroup, AwsEc2Vpc +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.kms import AwsKmsKey from fixlib.baseresources import ( BaseServerlessFunction, @@ -405,6 +406,7 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: clazz=AwsKmsKey, arn=self.function_kms_key_arn, ) + AwsGuardDutyFinding.set_findings(builder, self, "name") def update_resource_tag(self, client: AwsClient, key: str, value: str) -> bool: client.call( diff --git a/plugins/aws/fix_plugin_aws/resource/rds.py b/plugins/aws/fix_plugin_aws/resource/rds.py index c1427d463a..f6d644a02b 100644 --- a/plugins/aws/fix_plugin_aws/resource/rds.py +++ b/plugins/aws/fix_plugin_aws/resource/rds.py @@ -7,6 +7,7 @@ from fix_plugin_aws.resource.base import AwsApiSpec, AwsResource, GraphBuilder from fix_plugin_aws.resource.cloudwatch import AwsCloudwatchQuery, AwsCloudwatchMetricData, normalizer_factory from fix_plugin_aws.resource.ec2 import AwsEc2SecurityGroup, AwsEc2Subnet, AwsEc2Vpc +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.kinesis import AwsKinesisStream from fix_plugin_aws.resource.kms import AwsKmsKey from fix_plugin_aws.utils import ToDict, TagsValue @@ -598,6 +599,8 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: for key_reference in keys: builder.dependant_node(from_node=self, clazz=AwsKmsKey, id=AwsKmsKey.normalise_id(key_reference)) + AwsGuardDutyFinding.set_findings(builder, self, "id") + def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call( aws_service=self.api_spec.service, @@ -1039,6 +1042,7 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: ) if kinesis := self.rds_activity_stream_kinesis_stream_name: builder.add_edge(self, clazz=AwsKinesisStream, name=kinesis) + AwsGuardDutyFinding.set_findings(builder, self, "id") def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call( diff --git a/plugins/aws/fix_plugin_aws/resource/s3.py b/plugins/aws/fix_plugin_aws/resource/s3.py index bb52c3b8f9..db54ccec16 100644 --- a/plugins/aws/fix_plugin_aws/resource/s3.py +++ b/plugins/aws/fix_plugin_aws/resource/s3.py @@ -11,6 +11,7 @@ from fix_plugin_aws.aws_client import AwsClient from fix_plugin_aws.resource.base import AwsResource, AwsApiSpec, GraphBuilder, parse_json from fix_plugin_aws.resource.cloudwatch import AwsCloudwatchQuery, normalizer_factory +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.utils import tags_as_dict from fixlib.baseresources import ( BaseBucket, @@ -179,6 +180,7 @@ class AwsS3Bucket(AwsResource, BaseBucket, HasResourcePolicy): _kind_description: ClassVar[str] = "AWS S3 Bucket is a cloud storage service provided by Amazon Web Services. It stores and retrieves data objects, such as files, documents, and images. S3 Buckets organize data into containers, offering features like access control, versioning, and lifecycle management. Users can interact with S3 Buckets through APIs, SDKs, or the AWS Management Console." # fmt: skip _docs_url: ClassVar[str] = "https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-bucket.html" _kind_service: ClassVar[Optional[str]] = service_name + _reference_kinds: ClassVar[ModelReference] = {} api_spec: ClassVar[AwsApiSpec] = AwsApiSpec( service_name, "list-buckets", "Buckets", override_iam_permission="s3:ListAllMyBuckets" ) @@ -214,6 +216,9 @@ def called_collect_apis(cls) -> List[AwsApiSpec]: AwsApiSpec(service_name, "get-bucket-lifecycle-configuration"), ] + def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: + AwsGuardDutyFinding.set_findings(builder, self, "name") + @classmethod def collect(cls: Type[AwsResource], json: List[Json], builder: GraphBuilder) -> None: def add_tags(bucket: AwsS3Bucket) -> None: From 92ea7dc53ecc6fa0be6a2194317c72c5962e55c4 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 22 Oct 2024 10:01:13 +0000 Subject: [PATCH 10/18] updated tests --- plugins/aws/test/collector_test.py | 1 + .../resources/files/guardduty/get-findings__foo_foo.json | 4 ++-- plugins/aws/test/resources/guardduty.py | 7 ------- plugins/aws/test/resources/guardduty_test.py | 8 ++++++++ 4 files changed, 11 insertions(+), 9 deletions(-) delete mode 100644 plugins/aws/test/resources/guardduty.py create mode 100644 plugins/aws/test/resources/guardduty_test.py diff --git a/plugins/aws/test/collector_test.py b/plugins/aws/test/collector_test.py index c7d1c7bb7c..96da81117f 100644 --- a/plugins/aws/test/collector_test.py +++ b/plugins/aws/test/collector_test.py @@ -11,6 +11,7 @@ called_collect_apis, called_mutator_apis, ) +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.base import AwsResource, AwsApiSpec, GraphBuilder, AwsRegion from fix_plugin_aws.resource.ec2 import AwsEc2Instance from fixlib.baseresources import BaseResource diff --git a/plugins/aws/test/resources/files/guardduty/get-findings__foo_foo.json b/plugins/aws/test/resources/files/guardduty/get-findings__foo_foo.json index 1b4dc2bfd4..351ba4dd16 100644 --- a/plugins/aws/test/resources/files/guardduty/get-findings__foo_foo.json +++ b/plugins/aws/test/resources/files/guardduty/get-findings__foo_foo.json @@ -8,7 +8,7 @@ "Description": "foo", "Id": "foo", "Partition": "foo", - "Region": "foo", + "Region": "global", "Resource": { "AccessKeyDetails": { "AccessKeyId": "foo", @@ -83,7 +83,7 @@ }, "ImageDescription": "foo", "ImageId": "foo", - "InstanceId": "foo", + "InstanceId": "i-1", "InstanceState": "foo", "InstanceType": "foo", "OutpostArn": "foo", diff --git a/plugins/aws/test/resources/guardduty.py b/plugins/aws/test/resources/guardduty.py deleted file mode 100644 index 69a95b4db5..0000000000 --- a/plugins/aws/test/resources/guardduty.py +++ /dev/null @@ -1,7 +0,0 @@ -from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding -from test.resources import round_trip_for - - -def test_notebooks() -> None: - _, builder = round_trip_for(AwsGuardDutyFinding) - assert len(builder.resources_of(AwsGuardDutyFinding)) == 1 diff --git a/plugins/aws/test/resources/guardduty_test.py b/plugins/aws/test/resources/guardduty_test.py new file mode 100644 index 0000000000..707553b910 --- /dev/null +++ b/plugins/aws/test/resources/guardduty_test.py @@ -0,0 +1,8 @@ +from fix_plugin_aws.resource.ec2 import AwsEc2Instance +from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding +from test.resources import round_trip_for + + +def test_guardduty_findings() -> None: + collected, _ = round_trip_for(AwsEc2Instance, region_name="global", collect_also=[AwsGuardDutyFinding]) + assert len(collected._assessments) == 1 From 10177910dcea4348582dfa227201875fac035748 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 22 Oct 2024 11:08:28 +0000 Subject: [PATCH 11/18] feat: added concurrent features --- .../aws/fix_plugin_aws/resource/guardduty.py | 58 ++++++++++--------- plugins/aws/test/collector_test.py | 4 +- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index d7563265c3..11ca119654 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -1,3 +1,4 @@ +from concurrent.futures import as_completed from datetime import datetime, timezone from typing import ClassVar, Dict, List, Optional, Tuple, Type, Any import logging @@ -1284,32 +1285,25 @@ def set_findings(builder: GraphBuilder, resource_to_set: AwsResource, to_check: resource_to_set._assessments.append(Assessment("guard_duty", provider_findings)) def parse_finding(self, source: Json) -> Finding: - def get_severity() -> str: + def get_severity() -> Severity: if not self.finding_severity: - return "MEDIUM" + return Severity.medium if self.finding_severity <= 2: - return "INFORMATIONAL" + return Severity.info elif self.finding_severity <= 4: - return "LOW" + return Severity.low elif self.finding_severity <= 6: - return "MEDIUM" + return Severity.medium elif self.finding_severity <= 8: - return "HIGH" + return Severity.high else: - return "CRITICAL" - - severity_map = { - "INFORMATIONAL": Severity.info, - "LOW": Severity.low, - "MEDIUM": Severity.medium, - "HIGH": Severity.high, - "CRITICAL": Severity.critical, - } + return Severity.critical + finding_title = self.safe_name if not self.finding_severity: finding_severity = Severity.medium else: - finding_severity = severity_map.get(get_severity(), Severity.medium) + finding_severity = get_severity() description = self.description updated_at = self.mtime details = source.get("Service", {}) @@ -1369,28 +1363,40 @@ def check_type_and_adjust_id( return finding_resources try: - detector_ids = builder.client.list( - service_name, - "list-detectors", - "DetectorIds", - ) - for detector_id in detector_ids: - finding_ids = builder.client.list( + detector_ids = builder.client.list(service_name, "list-detectors", "DetectorIds") + finding_id_futures = { + builder.submit_work( + service_name, + builder.client.list, service_name, "list-findings", "FindingIds", expected_errors=["BadRequestException"], DetectorId=detector_id, - ) + ): detector_id + for detector_id in detector_ids + } + + for future in as_completed(finding_id_futures): + detector_id = finding_id_futures[future] + finding_ids = future.result() + chunk_futures = [] for chunk_ids in chunks(finding_ids, 49): - for finding in builder.client.list( + future = builder.submit_work( + service_name, + builder.client.list, service_name, "get-findings", "Findings", expected_errors=["BadRequestException"], DetectorId=detector_id, FindingIds=chunk_ids, - ): + ) + chunk_futures.append(future) + + for chunk_future in as_completed(chunk_futures): + findings = chunk_future.result() + for finding in findings: if finding.get("AccountId", None) == builder.account.id: if instance := AwsGuardDutyFinding.from_api(finding, builder): if fr := instance.finding_resource: diff --git a/plugins/aws/test/collector_test.py b/plugins/aws/test/collector_test.py index 96da81117f..b2f2ef5ae7 100644 --- a/plugins/aws/test/collector_test.py +++ b/plugins/aws/test/collector_test.py @@ -38,8 +38,8 @@ def count_kind(clazz: Type[AwsResource]) -> int: # make sure all threads have been joined assert len(threading.enumerate()) == 1 # ensure the correct number of nodes and edges - assert count_kind(AwsResource) == 262 - assert len(account_collector.graph.edges) == 577 + assert count_kind(AwsResource) == 261 + assert len(account_collector.graph.edges) == 575 assert len(account_collector.graph.deferred_edges) == 2 for node in account_collector.graph.nodes: if isinstance(node, AwsRegion): From 28e6e2f21307808979e10e52d4e8140344d1459b Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 22 Oct 2024 11:53:48 +0000 Subject: [PATCH 12/18] chore: delete unnecessary nodes deletion --- plugins/aws/fix_plugin_aws/collector.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/aws/fix_plugin_aws/collector.py b/plugins/aws/fix_plugin_aws/collector.py index d1de013277..01c27acd92 100644 --- a/plugins/aws/fix_plugin_aws/collector.py +++ b/plugins/aws/fix_plugin_aws/collector.py @@ -381,7 +381,6 @@ def rm_leaf_nodes(cls: Any, ignore_kinds: Optional[Type[Any]] = None, check_pred self.graph.remove_node(node) rm_leaf_nodes(bedrock.AwsBedrockFoundationModel, check_pred=False) - rm_leaf_nodes(guardduty.AwsGuardDutyFinding, check_pred=False) # remove regions that are not in use self.graph.remove_recursively(builder.nodes(AwsRegion, lambda r: r.compute_region_in_use(builder) is False)) From dc06447e7f57c6dbac9c9e3ade46ade3b084dc1d Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 22 Oct 2024 11:56:51 +0000 Subject: [PATCH 13/18] feat: merge main --- plugins/aws/test/collector_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/aws/test/collector_test.py b/plugins/aws/test/collector_test.py index b2f2ef5ae7..6254df5249 100644 --- a/plugins/aws/test/collector_test.py +++ b/plugins/aws/test/collector_test.py @@ -38,8 +38,8 @@ def count_kind(clazz: Type[AwsResource]) -> int: # make sure all threads have been joined assert len(threading.enumerate()) == 1 # ensure the correct number of nodes and edges - assert count_kind(AwsResource) == 261 - assert len(account_collector.graph.edges) == 575 + assert count_kind(AwsResource) == 260 + assert len(account_collector.graph.edges) == 574 assert len(account_collector.graph.deferred_edges) == 2 for node in account_collector.graph.nodes: if isinstance(node, AwsRegion): From 09a097ad174de49dc76ead3252f8f9e48ad5c375 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 29 Oct 2024 16:00:57 +0000 Subject: [PATCH 14/18] feat: updated AssessmentKey --- plugins/aws/fix_plugin_aws/resource/base.py | 12 +++++++++--- plugins/aws/fix_plugin_aws/resource/guardduty.py | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/aws/fix_plugin_aws/resource/base.py b/plugins/aws/fix_plugin_aws/resource/base.py index 0d74b259bb..11f899d3f2 100644 --- a/plugins/aws/fix_plugin_aws/resource/base.py +++ b/plugins/aws/fix_plugin_aws/resource/base.py @@ -65,8 +65,14 @@ def get_client(config: Config, resource: BaseResource) -> AwsClient: "region_id": lambda n: n.region().id, } -# Type alias for a tuple representing provider, region, and resource type -AssessmentKey = Tuple[str, str, str] + +@define(slots=True, frozen=True) +class AssessmentKey: + provider: str + region: str + resource_type: str + + # Type alias for the inner dictionary that maps resource ID to a list of findings ResourceFindings = Dict[str, List[Finding]] @@ -509,7 +515,7 @@ def suppress(self, message: str) -> SuppressWithFeedback: return SuppressWithFeedback(message, self.core_feedback, log) def add_finding(self, provider: str, class_name: str, region: str, class_id: str, finding: Finding) -> None: - self._assessment_findings[(provider, region, class_name)][class_id].append(finding) + self._assessment_findings[AssessmentKey(provider, region, class_name)][class_id].append(finding) def submit_work(self, service: str, fn: Callable[..., T], *args: Any, **kwargs: Any) -> Future[T]: """ diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 11ca119654..bd4162439f 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -6,7 +6,7 @@ from attrs import define, field from boto3.exceptions import Boto3Error -from fix_plugin_aws.resource.base import AwsResource, GraphBuilder +from fix_plugin_aws.resource.base import AssessmentKey, AwsResource, GraphBuilder from fixlib.baseresources import Assessment, Finding, PhantomBaseResource, Severity from fixlib.json_bender import F, S, AsInt, Bend, Bender, ForallBend @@ -1278,7 +1278,7 @@ def set_findings(builder: GraphBuilder, resource_to_set: AwsResource, to_check: else: return provider_findings = builder._assessment_findings.get( - ("guard_duty", resource_to_set.region().id, resource_to_set.__class__.__name__), {} + AssessmentKey("guard_duty", resource_to_set.region().id, resource_to_set.__class__.__name__), {} ).get(id_or_arn_or_name, []) if provider_findings: # Set the findings in the resource's _assessments dictionary From 4ad2ffcb2c46fbc6e604364587f6250087c77f6b Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 31 Oct 2024 15:11:39 +0000 Subject: [PATCH 15/18] feat: reimplemented a way to collect guardduty --- plugins/aws/fix_plugin_aws/resource/base.py | 11 - plugins/aws/fix_plugin_aws/resource/ec2.py | 4 - plugins/aws/fix_plugin_aws/resource/ecr.py | 1 - plugins/aws/fix_plugin_aws/resource/ecs.py | 2 - plugins/aws/fix_plugin_aws/resource/eks.py | 2 - .../aws/fix_plugin_aws/resource/guardduty.py | 755 +----------------- .../aws/fix_plugin_aws/resource/lambda_.py | 2 - plugins/aws/fix_plugin_aws/resource/rds.py | 4 - plugins/aws/fix_plugin_aws/resource/s3.py | 5 - plugins/aws/test/collector_test.py | 1 - 10 files changed, 41 insertions(+), 746 deletions(-) diff --git a/plugins/aws/fix_plugin_aws/resource/base.py b/plugins/aws/fix_plugin_aws/resource/base.py index 11f899d3f2..7a487b24c7 100644 --- a/plugins/aws/fix_plugin_aws/resource/base.py +++ b/plugins/aws/fix_plugin_aws/resource/base.py @@ -66,17 +66,6 @@ def get_client(config: Config, resource: BaseResource) -> AwsClient: } -@define(slots=True, frozen=True) -class AssessmentKey: - provider: str - region: str - resource_type: str - - -# Type alias for the inner dictionary that maps resource ID to a list of findings -ResourceFindings = Dict[str, List[Finding]] - - def parse_json( json: Json, clazz: Type[T], builder: GraphBuilder, mapping: Optional[Dict[str, Bender]] = None ) -> Optional[T]: diff --git a/plugins/aws/fix_plugin_aws/resource/ec2.py b/plugins/aws/fix_plugin_aws/resource/ec2.py index 4309253a39..881b234308 100644 --- a/plugins/aws/fix_plugin_aws/resource/ec2.py +++ b/plugins/aws/fix_plugin_aws/resource/ec2.py @@ -18,7 +18,6 @@ operations_to_iops, normalizer_factory, ) -from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.iam import AwsIamInstanceProfile from fix_plugin_aws.resource.kms import AwsKmsKey from fix_plugin_aws.resource.s3 import AwsS3Bucket @@ -706,7 +705,6 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: clazz=AwsKmsKey, id=AwsKmsKey.normalise_id(self.volume_kms_key_id), ) - AwsGuardDutyFinding.set_findings(builder, self, "arn") def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call( @@ -1524,8 +1522,6 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: if iam_profile := self.instance_iam_instance_profile: builder.add_edge(self, reverse=True, clazz=AwsIamInstanceProfile, arn=iam_profile.arn) - AwsGuardDutyFinding.set_findings(builder, self, "id") - def delete_resource(self, client: AwsClient, graph: Graph) -> bool: if self.instance_status == InstanceStatus.TERMINATED: self.log("Instance is already terminated") diff --git a/plugins/aws/fix_plugin_aws/resource/ecr.py b/plugins/aws/fix_plugin_aws/resource/ecr.py index 21c6811fd2..592f1f26a7 100644 --- a/plugins/aws/fix_plugin_aws/resource/ecr.py +++ b/plugins/aws/fix_plugin_aws/resource/ecr.py @@ -34,7 +34,6 @@ class AwsEcrRepository(AwsResource, HasResourcePolicy): _kind_service: ClassVar[Optional[str]] = service_name _metadata: ClassVar[Dict[str, Any]] = {"icon": "repository", "group": "compute"} _aws_metadata: ClassVar[Dict[str, Any]] = {"provider_link_tpl": "https://{region_id}.console.aws.amazon.com/ecr/repositories/{name}?region={region}", "arn_tpl": "arn:{partition}:ecr:{region}:{account}:repository/{name}"} # fmt: skip - _reference_kinds: ClassVar[ModelReference] = {} api_spec: ClassVar[AwsApiSpec] = AwsApiSpec("ecr", "describe-repositories", "repositories") public_spec: ClassVar[AwsApiSpec] = AwsApiSpec("ecr-public", "describe-repositories", "repositories") mapping: ClassVar[Dict[str, Bender]] = { diff --git a/plugins/aws/fix_plugin_aws/resource/ecs.py b/plugins/aws/fix_plugin_aws/resource/ecs.py index 9d52e0c5e1..7570e20cb3 100644 --- a/plugins/aws/fix_plugin_aws/resource/ecs.py +++ b/plugins/aws/fix_plugin_aws/resource/ecs.py @@ -6,7 +6,6 @@ from attrs import define, field from fix_plugin_aws.aws_client import AwsClient from fix_plugin_aws.resource.autoscaling import AwsAutoScalingGroup -from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.utils import TagsValue, ToDict from fix_plugin_aws.resource.base import AwsResource, GraphBuilder, AwsApiSpec @@ -2111,7 +2110,6 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: builder.dependant_node(self, clazz=AwsKmsKey, id=AwsKmsKey.normalise_id(exc.kms_key_id)) if exc.log_configuration and exc.log_configuration.s3_bucket_name: builder.add_edge(self, clazz=AwsS3Bucket, name=exc.log_configuration.s3_bucket_name) - AwsGuardDutyFinding.set_findings(builder, self, "arn") def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call(aws_service=self.api_spec.service, action="delete-cluster", result_name=None, cluster=self.arn) diff --git a/plugins/aws/fix_plugin_aws/resource/eks.py b/plugins/aws/fix_plugin_aws/resource/eks.py index a5ea970e9f..9ad54520b6 100644 --- a/plugins/aws/fix_plugin_aws/resource/eks.py +++ b/plugins/aws/fix_plugin_aws/resource/eks.py @@ -6,7 +6,6 @@ from fix_plugin_aws.resource.autoscaling import AwsAutoScalingGroup from fix_plugin_aws.resource.base import AwsResource, GraphBuilder, AwsApiSpec -from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.iam import AwsIamRole from fix_plugin_aws.aws_client import AwsClient from fixlib.baseresources import ModelReference, BaseManagedKubernetesClusterProvider @@ -478,7 +477,6 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: builder.dependant_node( self, reverse=True, delete_same_as_default=True, clazz=AwsIamRole, arn=self.cluster_role_arn ) - AwsGuardDutyFinding.set_findings(builder, self, "arn") def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call(aws_service=self.api_spec.service, action="delete-cluster", result_name=None, name=self.name) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index bd4162439f..4e333f0cba 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -1,20 +1,28 @@ from concurrent.futures import as_completed from datetime import datetime, timezone +from functools import partial from typing import ClassVar, Dict, List, Optional, Tuple, Type, Any import logging from attrs import define, field from boto3.exceptions import Boto3Error -from fix_plugin_aws.resource.base import AssessmentKey, AwsResource, GraphBuilder +from fix_plugin_aws.resource.base import AwsResource, GraphBuilder +from fix_plugin_aws.resource.ec2 import AwsEc2Instance, AwsEc2Volume +from fix_plugin_aws.resource.ecs import AwsEcsCluster +from fix_plugin_aws.resource.eks import AwsEksCluster +from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction +from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance +from fix_plugin_aws.resource.s3 import AwsS3Bucket -from fixlib.baseresources import Assessment, Finding, PhantomBaseResource, Severity +from fixlib.baseresources import Finding, PhantomBaseResource, Severity from fixlib.json_bender import F, S, AsInt, Bend, Bender, ForallBend from fixlib.types import Json from fixlib.utils import chunks, utc_str log = logging.getLogger("fix.plugins.aws") service_name = "guardduty" +amazon_guardduty = "amazon_guard_duty" @define(eq=False, slots=False) @@ -567,664 +575,10 @@ class AwsGuardDutyResource: lambda_details: Optional[AwsGuardDutyLambdaDetails] = field(default=None, metadata={"description": "Contains information about the Lambda function that was involved in a finding."}) # fmt: skip -@define(eq=False, slots=False) -class AwsGuardDutyCountry: - kind: ClassVar[str] = "aws_guard_duty_country" - mapping: ClassVar[Dict[str, Bender]] = {"country_code": S("CountryCode"), "country_name": S("CountryName")} - country_code: Optional[str] = field(default=None, metadata={"description": "The country code of the remote IP address."}) # fmt: skip - country_name: Optional[str] = field(default=None, metadata={"description": "The country name of the remote IP address."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyGeoLocation: - kind: ClassVar[str] = "aws_guard_duty_geo_location" - mapping: ClassVar[Dict[str, Bender]] = {"lat": S("Lat"), "lon": S("Lon")} - lat: Optional[float] = field(default=None, metadata={"description": "The latitude information of the remote IP address."}) # fmt: skip - lon: Optional[float] = field(default=None, metadata={"description": "The longitude information of the remote IP address."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyOrganization: - kind: ClassVar[str] = "aws_guard_duty_organization" - mapping: ClassVar[Dict[str, Bender]] = {"asn": S("Asn"), "asn_org": S("AsnOrg"), "isp": S("Isp"), "org": S("Org")} - asn: Optional[str] = field(default=None, metadata={"description": "The Autonomous System Number (ASN) of the internet provider of the remote IP address."}) # fmt: skip - asn_org: Optional[str] = field(default=None, metadata={"description": "The organization that registered this ASN."}) # fmt: skip - isp: Optional[str] = field(default=None, metadata={"description": "The ISP information for the internet provider."}) # fmt: skip - org: Optional[str] = field(default=None, metadata={"description": "The name of the internet provider."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyRemoteIpDetails: - kind: ClassVar[str] = "aws_guard_duty_remote_ip_details" - mapping: ClassVar[Dict[str, Bender]] = { - "city": S("City", "CityName"), - "country": S("Country") >> Bend(AwsGuardDutyCountry.mapping), - "geo_location": S("GeoLocation") >> Bend(AwsGuardDutyGeoLocation.mapping), - "ip_address_v4": S("IpAddressV4"), - "ip_address_v6": S("IpAddressV6"), - "organization": S("Organization") >> Bend(AwsGuardDutyOrganization.mapping), - } - city: Optional[str] = field(default=None, metadata={"description": "The city information of the remote IP address."}) # fmt: skip - country: Optional[AwsGuardDutyCountry] = field(default=None, metadata={"description": "The country code of the remote IP address."}) # fmt: skip - geo_location: Optional[AwsGuardDutyGeoLocation] = field(default=None, metadata={"description": "The location information of the remote IP address."}) # fmt: skip - ip_address_v4: Optional[str] = field(default=None, metadata={"description": "The IPv4 remote address of the connection."}) # fmt: skip - ip_address_v6: Optional[str] = field(default=None, metadata={"description": "The IPv6 remote address of the connection."}) # fmt: skip - organization: Optional[AwsGuardDutyOrganization] = field(default=None, metadata={"description": "The ISP organization information of the remote IP address."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyRemoteAccountDetails: - kind: ClassVar[str] = "aws_guard_duty_remote_account_details" - mapping: ClassVar[Dict[str, Bender]] = {"account_id": S("AccountId"), "affiliated": S("Affiliated")} - account_id: Optional[str] = field(default=None, metadata={"description": "The Amazon Web Services account ID of the remote API caller."}) # fmt: skip - affiliated: Optional[bool] = field(default=None, metadata={"description": "Details on whether the Amazon Web Services account of the remote API caller is related to your GuardDuty environment. If this value is True the API caller is affiliated to your account in some way. If it is False the API caller is from outside your environment."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyAwsApiCallAction: - kind: ClassVar[str] = "aws_guard_duty_aws_api_call_action" - mapping: ClassVar[Dict[str, Bender]] = { - "api": S("Api"), - "caller_type": S("CallerType"), - "domain_details": S("DomainDetails", "Domain"), - "error_code": S("ErrorCode"), - "user_agent": S("UserAgent"), - "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), - "service_name": S("ServiceName"), - "remote_account_details": S("RemoteAccountDetails") >> Bend(AwsGuardDutyRemoteAccountDetails.mapping), - "affected_resources": S("AffectedResources"), - } - api: Optional[str] = field(default=None, metadata={"description": "The Amazon Web Services API name."}) # fmt: skip - caller_type: Optional[str] = field(default=None, metadata={"description": "The Amazon Web Services API caller type."}) # fmt: skip - domain_details: Optional[str] = field(default=None, metadata={"description": "The domain information for the Amazon Web Services API call."}) # fmt: skip - error_code: Optional[str] = field(default=None, metadata={"description": "The error code of the failed Amazon Web Services API action."}) # fmt: skip - user_agent: Optional[str] = field(default=None, metadata={"description": "The agent through which the API request was made."}) # fmt: skip - remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "The remote IP information of the connection that initiated the Amazon Web Services API call."}) # fmt: skip - service_name: Optional[str] = field(default=None, metadata={"description": "The Amazon Web Services service name whose API was invoked."}) # fmt: skip - remote_account_details: Optional[AwsGuardDutyRemoteAccountDetails] = field(default=None, metadata={"description": "The details of the Amazon Web Services account that made the API call. This field appears if the call was made from outside your account."}) # fmt: skip - affected_resources: Optional[Dict[str, str]] = field(default=None, metadata={"description": "The details of the Amazon Web Services account that made the API call. This field identifies the resources that were affected by this API call."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyDnsRequestAction: - kind: ClassVar[str] = "aws_guard_duty_dns_request_action" - mapping: ClassVar[Dict[str, Bender]] = { - "domain": S("Domain"), - "protocol": S("Protocol"), - "blocked": S("Blocked"), - "domain_with_suffix": S("DomainWithSuffix"), - } - domain: Optional[str] = field(default=None, metadata={"description": "The domain information for the DNS query."}) # fmt: skip - protocol: Optional[str] = field(default=None, metadata={"description": "The network connection protocol observed in the activity that prompted GuardDuty to generate the finding."}) # fmt: skip - blocked: Optional[bool] = field(default=None, metadata={"description": "Indicates whether the targeted port is blocked."}) # fmt: skip - domain_with_suffix: Optional[str] = field(default=None, metadata={"description": "The second and top level domain involved in the activity that potentially prompted GuardDuty to generate this finding. For a list of top-level and second-level domains, see public suffix list."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyLocalPortDetails: - kind: ClassVar[str] = "aws_guard_duty_local_port_details" - mapping: ClassVar[Dict[str, Bender]] = {"port": S("Port"), "port_name": S("PortName")} - port: Optional[int] = field(default=None, metadata={"description": "The port number of the local connection."}) # fmt: skip - port_name: Optional[str] = field(default=None, metadata={"description": "The port name of the local connection."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyLocalIpDetails: - kind: ClassVar[str] = "aws_guard_duty_local_ip_details" - mapping: ClassVar[Dict[str, Bender]] = {"ip_address_v4": S("IpAddressV4"), "ip_address_v6": S("IpAddressV6")} - ip_address_v4: Optional[str] = field(default=None, metadata={"description": "The IPv4 local address of the connection."}) # fmt: skip - ip_address_v6: Optional[str] = field(default=None, metadata={"description": "The IPv6 local address of the connection."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyRemotePortDetails: - kind: ClassVar[str] = "aws_guard_duty_remote_port_details" - mapping: ClassVar[Dict[str, Bender]] = {"port": S("Port"), "port_name": S("PortName")} - port: Optional[int] = field(default=None, metadata={"description": "The port number of the remote connection."}) # fmt: skip - port_name: Optional[str] = field(default=None, metadata={"description": "The port name of the remote connection."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyNetworkConnectionAction: - kind: ClassVar[str] = "aws_guard_duty_network_connection_action" - mapping: ClassVar[Dict[str, Bender]] = { - "blocked": S("Blocked"), - "connection_direction": S("ConnectionDirection"), - "local_port_details": S("LocalPortDetails") >> Bend(AwsGuardDutyLocalPortDetails.mapping), - "protocol": S("Protocol"), - "local_ip_details": S("LocalIpDetails") >> Bend(AwsGuardDutyLocalIpDetails.mapping), - "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), - "remote_port_details": S("RemotePortDetails") >> Bend(AwsGuardDutyRemotePortDetails.mapping), - } - blocked: Optional[bool] = field(default=None, metadata={"description": "Indicates whether EC2 blocked the network connection to your instance."}) # fmt: skip - connection_direction: Optional[str] = field(default=None, metadata={"description": "The network connection direction."}) # fmt: skip - local_port_details: Optional[AwsGuardDutyLocalPortDetails] = field(default=None, metadata={"description": "The local port information of the connection."}) # fmt: skip - protocol: Optional[str] = field(default=None, metadata={"description": "The network connection protocol."}) # fmt: skip - local_ip_details: Optional[AwsGuardDutyLocalIpDetails] = field(default=None, metadata={"description": "The local IP information of the connection."}) # fmt: skip - remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "The remote IP information of the connection."}) # fmt: skip - remote_port_details: Optional[AwsGuardDutyRemotePortDetails] = field(default=None, metadata={"description": "The remote port information of the connection."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyPortProbeDetail: - kind: ClassVar[str] = "aws_guard_duty_port_probe_detail" - mapping: ClassVar[Dict[str, Bender]] = { - "local_port_details": S("LocalPortDetails") >> Bend(AwsGuardDutyLocalPortDetails.mapping), - "local_ip_details": S("LocalIpDetails") >> Bend(AwsGuardDutyLocalIpDetails.mapping), - "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), - } - local_port_details: Optional[AwsGuardDutyLocalPortDetails] = field(default=None, metadata={"description": "The local port information of the connection."}) # fmt: skip - local_ip_details: Optional[AwsGuardDutyLocalIpDetails] = field(default=None, metadata={"description": "The local IP information of the connection."}) # fmt: skip - remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "The remote IP information of the connection."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyPortProbeAction: - kind: ClassVar[str] = "aws_guard_duty_port_probe_action" - mapping: ClassVar[Dict[str, Bender]] = { - "blocked": S("Blocked"), - "port_probe_details": S("PortProbeDetails", default=[]) >> ForallBend(AwsGuardDutyPortProbeDetail.mapping), - } - blocked: Optional[bool] = field(default=None, metadata={"description": "Indicates whether EC2 blocked the port probe to the instance, such as with an ACL."}) # fmt: skip - port_probe_details: Optional[List[AwsGuardDutyPortProbeDetail]] = field(factory=list, metadata={"description": "A list of objects related to port probe details."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyKubernetesApiCallAction: - kind: ClassVar[str] = "aws_guard_duty_kubernetes_api_call_action" - mapping: ClassVar[Dict[str, Bender]] = { - "request_uri": S("RequestUri"), - "verb": S("Verb"), - "source_ips": S("SourceIps", default=[]), - "user_agent": S("UserAgent"), - "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), - "status_code": S("StatusCode"), - "parameters": S("Parameters"), - "resource": S("Resource"), - "subresource": S("Subresource"), - "namespace": S("Namespace"), - "resource_name": S("ResourceName"), - } - request_uri: Optional[str] = field(default=None, metadata={"description": "The Kubernetes API request URI."}) # fmt: skip - verb: Optional[str] = field(default=None, metadata={"description": "The Kubernetes API request HTTP verb."}) # fmt: skip - source_ips: Optional[List[str]] = field(factory=list, metadata={"description": "The IP of the Kubernetes API caller and the IPs of any proxies or load balancers between the caller and the API endpoint."}) # fmt: skip - user_agent: Optional[str] = field(default=None, metadata={"description": "The user agent of the caller of the Kubernetes API."}) # fmt: skip - remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "Contains information about the remote IP address of the connection."}) # fmt: skip - status_code: Optional[int] = field(default=None, metadata={"description": "The resulting HTTP response code of the Kubernetes API call action."}) # fmt: skip - parameters: Optional[str] = field(default=None, metadata={"description": "Parameters related to the Kubernetes API call action."}) # fmt: skip - resource: Optional[str] = field(default=None, metadata={"description": "The resource component in the Kubernetes API call action."}) # fmt: skip - subresource: Optional[str] = field(default=None, metadata={"description": "The name of the sub-resource in the Kubernetes API call action."}) # fmt: skip - namespace: Optional[str] = field(default=None, metadata={"description": "The name of the namespace where the Kubernetes API call action takes place."}) # fmt: skip - resource_name: Optional[str] = field(default=None, metadata={"description": "The name of the resource in the Kubernetes API call action."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyLoginAttribute: - kind: ClassVar[str] = "aws_guard_duty_login_attribute" - mapping: ClassVar[Dict[str, Bender]] = { - "user": S("User"), - "application": S("Application"), - "failed_login_attempts": S("FailedLoginAttempts"), - "successful_login_attempts": S("SuccessfulLoginAttempts"), - } - user: Optional[str] = field(default=None, metadata={"description": "Indicates the user name which attempted to log in."}) # fmt: skip - application: Optional[str] = field(default=None, metadata={"description": "Indicates the application name used to attempt log in."}) # fmt: skip - failed_login_attempts: Optional[int] = field(default=None, metadata={"description": "Represents the sum of failed (unsuccessful) login attempts made to establish a connection to the database instance."}) # fmt: skip - successful_login_attempts: Optional[int] = field(default=None, metadata={"description": "Represents the sum of successful connections (a correct combination of login attributes) made to the database instance by the actor."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyRdsLoginAttemptAction: - kind: ClassVar[str] = "aws_guard_duty_rds_login_attempt_action" - mapping: ClassVar[Dict[str, Bender]] = { - "remote_ip_details": S("RemoteIpDetails") >> Bend(AwsGuardDutyRemoteIpDetails.mapping), - "login_attributes": S("LoginAttributes", default=[]) >> ForallBend(AwsGuardDutyLoginAttribute.mapping), - } - remote_ip_details: Optional[AwsGuardDutyRemoteIpDetails] = field(default=None, metadata={"description": "Contains information about the remote IP address of the connection."}) # fmt: skip - login_attributes: Optional[List[AwsGuardDutyLoginAttribute]] = field(factory=list, metadata={"description": "Indicates the login attributes used in the login attempt."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyKubernetesPermissionCheckedDetails: - kind: ClassVar[str] = "aws_guard_duty_kubernetes_permission_checked_details" - mapping: ClassVar[Dict[str, Bender]] = { - "verb": S("Verb"), - "resource": S("Resource"), - "namespace": S("Namespace"), - "allowed": S("Allowed"), - } - verb: Optional[str] = field(default=None, metadata={"description": "The verb component of the Kubernetes API call. For example, when you check whether or not you have the permission to call the CreatePod API, the verb component will be Create."}) # fmt: skip - resource: Optional[str] = field(default=None, metadata={"description": "The Kubernetes resource with which your Kubernetes API call will interact."}) # fmt: skip - namespace: Optional[str] = field(default=None, metadata={"description": "The namespace where the Kubernetes API action will take place."}) # fmt: skip - allowed: Optional[bool] = field(default=None, metadata={"description": "Information whether the user has the permission to call the Kubernetes API."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyKubernetesRoleBindingDetails: - kind: ClassVar[str] = "aws_guard_duty_kubernetes_role_binding_details" - mapping: ClassVar[Dict[str, Bender]] = { - "role_kind": S("Kind"), - "name": S("Name"), - "uid": S("Uid"), - "role_ref_name": S("RoleRefName"), - "role_ref_kind": S("RoleRefKind"), - } - role_kind: Optional[str] = field(default=None, metadata={"description": "The kind of the role. For role binding, this value will be RoleBinding."}) # fmt: skip - name: Optional[str] = field(default=None, metadata={"description": "The name of the RoleBinding."}) # fmt: skip - uid: Optional[str] = field(default=None, metadata={"description": "The unique identifier of the role binding."}) # fmt: skip - role_ref_name: Optional[str] = field(default=None, metadata={"description": "The name of the role being referenced. This must match the name of the Role or ClusterRole that you want to bind to."}) # fmt: skip - role_ref_kind: Optional[str] = field(default=None, metadata={"description": "The type of the role being referenced. This could be either Role or ClusterRole."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyKubernetesRoleDetails: - kind: ClassVar[str] = "aws_guard_duty_kubernetes_role_details" - mapping: ClassVar[Dict[str, Bender]] = {"role_kind": S("Kind"), "name": S("Name"), "uid": S("Uid")} - role_kind: Optional[str] = field(default=None, metadata={"description": "The kind of role. For this API, the value of kind will be Role."}) # fmt: skip - name: Optional[str] = field(default=None, metadata={"description": "The name of the Kubernetes role."}) # fmt: skip - uid: Optional[str] = field(default=None, metadata={"description": "The unique identifier of the Kubernetes role name."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyAction: - kind: ClassVar[str] = "aws_guard_duty_action" - mapping: ClassVar[Dict[str, Bender]] = { - "action_type": S("ActionType"), - "aws_api_call_action": S("AwsApiCallAction") >> Bend(AwsGuardDutyAwsApiCallAction.mapping), - "dns_request_action": S("DnsRequestAction") >> Bend(AwsGuardDutyDnsRequestAction.mapping), - "network_connection_action": S("NetworkConnectionAction") >> Bend(AwsGuardDutyNetworkConnectionAction.mapping), - "port_probe_action": S("PortProbeAction") >> Bend(AwsGuardDutyPortProbeAction.mapping), - "kubernetes_api_call_action": S("KubernetesApiCallAction") >> Bend(AwsGuardDutyKubernetesApiCallAction.mapping), - "rds_login_attempt_action": S("RdsLoginAttemptAction") >> Bend(AwsGuardDutyRdsLoginAttemptAction.mapping), - "kubernetes_permission_checked_details": S("KubernetesPermissionCheckedDetails") - >> Bend(AwsGuardDutyKubernetesPermissionCheckedDetails.mapping), - "kubernetes_role_binding_details": S("KubernetesRoleBindingDetails") - >> Bend(AwsGuardDutyKubernetesRoleBindingDetails.mapping), - "kubernetes_role_details": S("KubernetesRoleDetails") >> Bend(AwsGuardDutyKubernetesRoleDetails.mapping), - } - action_type: Optional[str] = field(default=None, metadata={"description": "The GuardDuty finding activity type."}) # fmt: skip - aws_api_call_action: Optional[AwsGuardDutyAwsApiCallAction] = field(default=None, metadata={"description": "Information about the AWS_API_CALL action described in this finding."}) # fmt: skip - dns_request_action: Optional[AwsGuardDutyDnsRequestAction] = field(default=None, metadata={"description": "Information about the DNS_REQUEST action described in this finding."}) # fmt: skip - network_connection_action: Optional[AwsGuardDutyNetworkConnectionAction] = field(default=None, metadata={"description": "Information about the NETWORK_CONNECTION action described in this finding."}) # fmt: skip - port_probe_action: Optional[AwsGuardDutyPortProbeAction] = field(default=None, metadata={"description": "Information about the PORT_PROBE action described in this finding."}) # fmt: skip - kubernetes_api_call_action: Optional[AwsGuardDutyKubernetesApiCallAction] = field(default=None, metadata={"description": "Information about the Kubernetes API call action described in this finding."}) # fmt: skip - rds_login_attempt_action: Optional[AwsGuardDutyRdsLoginAttemptAction] = field(default=None, metadata={"description": "Information about RDS_LOGIN_ATTEMPT action described in this finding."}) # fmt: skip - kubernetes_permission_checked_details: Optional[AwsGuardDutyKubernetesPermissionCheckedDetails] = field(default=None, metadata={"description": "Information whether the user has the permission to use a specific Kubernetes API."}) # fmt: skip - kubernetes_role_binding_details: Optional[AwsGuardDutyKubernetesRoleBindingDetails] = field(default=None, metadata={"description": "Information about the role binding that grants the permission defined in a Kubernetes role."}) # fmt: skip - kubernetes_role_details: Optional[AwsGuardDutyKubernetesRoleDetails] = field(default=None, metadata={"description": "Information about the Kubernetes role name and role type."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyThreatIntelligenceDetail: - kind: ClassVar[str] = "aws_guard_duty_threat_intelligence_detail" - mapping: ClassVar[Dict[str, Bender]] = { - "threat_list_name": S("ThreatListName"), - "threat_names": S("ThreatNames", default=[]), - "threat_file_sha256": S("ThreatFileSha256"), - } - threat_list_name: Optional[str] = field(default=None, metadata={"description": "The name of the threat intelligence list that triggered the finding."}) # fmt: skip - threat_names: Optional[List[str]] = field(factory=list, metadata={"description": "A list of names of the threats in the threat intelligence list that triggered the finding."}) # fmt: skip - threat_file_sha256: Optional[str] = field(default=None, metadata={"description": "SHA256 of the file that generated the finding."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyEvidence: - kind: ClassVar[str] = "aws_guard_duty_evidence" - mapping: ClassVar[Dict[str, Bender]] = { - "threat_intelligence_details": S("ThreatIntelligenceDetails", default=[]) - >> ForallBend(AwsGuardDutyThreatIntelligenceDetail.mapping) - } - threat_intelligence_details: Optional[List[AwsGuardDutyThreatIntelligenceDetail]] = field(factory=list, metadata={"description": "A list of threat intelligence details related to the evidence."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyServiceAdditionalInfo: - kind: ClassVar[str] = "aws_guard_duty_service_additional_info" - mapping: ClassVar[Dict[str, Bender]] = {"value": S("Value"), "type": S("Type")} - value: Optional[str] = field(default=None, metadata={"description": "This field specifies the value of the additional information."}) # fmt: skip - type: Optional[str] = field(default=None, metadata={"description": "Describes the type of the additional information."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyScannedItemCount: - kind: ClassVar[str] = "aws_guard_duty_scanned_item_count" - mapping: ClassVar[Dict[str, Bender]] = {"total_gb": S("TotalGb"), "files": S("Files"), "volumes": S("Volumes")} - total_gb: Optional[int] = field(default=None, metadata={"description": "Total GB of files scanned for malware."}) # fmt: skip - files: Optional[int] = field(default=None, metadata={"description": "Number of files scanned."}) # fmt: skip - volumes: Optional[int] = field(default=None, metadata={"description": "Total number of scanned volumes."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyHighestSeverityThreatDetails: - kind: ClassVar[str] = "aws_guard_duty_highest_severity_threat_details" - mapping: ClassVar[Dict[str, Bender]] = { - "severity": S("Severity"), - "threat_name": S("ThreatName"), - "count": S("Count"), - } - severity: Optional[str] = field(default=None, metadata={"description": "Severity level of the highest severity threat detected."}) # fmt: skip - threat_name: Optional[str] = field(default=None, metadata={"description": "Threat name of the highest severity threat detected as part of the malware scan."}) # fmt: skip - count: Optional[int] = field(default=None, metadata={"description": "Total number of infected files with the highest severity threat detected."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyScanFilePath: - kind: ClassVar[str] = "aws_guard_duty_scan_file_path" - mapping: ClassVar[Dict[str, Bender]] = { - "file_path": S("FilePath"), - "volume_arn": S("VolumeArn"), - "hash": S("Hash"), - "file_name": S("FileName"), - } - file_path: Optional[str] = field(default=None, metadata={"description": "The file path of the infected file."}) # fmt: skip - volume_arn: Optional[str] = field(default=None, metadata={"description": "EBS volume ARN details of the infected file."}) # fmt: skip - hash: Optional[str] = field(default=None, metadata={"description": "The hash value of the infected file."}) # fmt: skip - file_name: Optional[str] = field(default=None, metadata={"description": "File name of the infected file."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyScanThreatName: - kind: ClassVar[str] = "aws_guard_duty_scan_threat_name" - mapping: ClassVar[Dict[str, Bender]] = { - "name": S("Name"), - "severity": S("Severity"), - "item_count": S("ItemCount"), - "file_paths": S("FilePaths", default=[]) >> ForallBend(AwsGuardDutyScanFilePath.mapping), - } - name: Optional[str] = field(default=None, metadata={"description": "The name of the identified threat."}) # fmt: skip - severity: Optional[str] = field(default=None, metadata={"description": "Severity of threat identified as part of the malware scan."}) # fmt: skip - item_count: Optional[int] = field(default=None, metadata={"description": "Total number of files infected with given threat."}) # fmt: skip - file_paths: Optional[List[AwsGuardDutyScanFilePath]] = field(factory=list, metadata={"description": "List of infected files in EBS volume with details."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyThreatDetectedByName: - kind: ClassVar[str] = "aws_guard_duty_threat_detected_by_name" - mapping: ClassVar[Dict[str, Bender]] = { - "item_count": S("ItemCount"), - "unique_threat_name_count": S("UniqueThreatNameCount"), - "shortened": S("Shortened"), - "threat_names": S("ThreatNames", default=[]) >> ForallBend(AwsGuardDutyScanThreatName.mapping), - } - item_count: Optional[int] = field(default=None, metadata={"description": "Total number of infected files identified."}) # fmt: skip - unique_threat_name_count: Optional[int] = field(default=None, metadata={"description": "Total number of unique threats by name identified, as part of the malware scan."}) # fmt: skip - shortened: Optional[bool] = field(default=None, metadata={"description": "Flag to determine if the finding contains every single infected file-path and/or every threat."}) # fmt: skip - threat_names: Optional[List[AwsGuardDutyScanThreatName]] = field(factory=list, metadata={"description": "List of identified threats with details, organized by threat name."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyScanDetections: - kind: ClassVar[str] = "aws_guard_duty_scan_detections" - mapping: ClassVar[Dict[str, Bender]] = { - "scanned_item_count": S("ScannedItemCount") >> Bend(AwsGuardDutyScannedItemCount.mapping), - "threats_detected_item_count": S("ThreatsDetectedItemCount", "Files"), - "highest_severity_threat_details": S("HighestSeverityThreatDetails") - >> Bend(AwsGuardDutyHighestSeverityThreatDetails.mapping), - "threat_detected_by_name": S("ThreatDetectedByName") >> Bend(AwsGuardDutyThreatDetectedByName.mapping), - } - scanned_item_count: Optional[AwsGuardDutyScannedItemCount] = field(default=None, metadata={"description": "Total number of scanned files."}) # fmt: skip - threats_detected_item_count: Optional[int] = field(default=None, metadata={"description": "Total number of infected files."}) # fmt: skip - highest_severity_threat_details: Optional[AwsGuardDutyHighestSeverityThreatDetails] = field(default=None, metadata={"description": "Details of the highest severity threat detected during malware scan and number of infected files."}) # fmt: skip - threat_detected_by_name: Optional[AwsGuardDutyThreatDetectedByName] = field(default=None, metadata={"description": "Contains details about identified threats organized by threat name."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyEbsVolumeScanDetails: - kind: ClassVar[str] = "aws_guard_duty_ebs_volume_scan_details" - mapping: ClassVar[Dict[str, Bender]] = { - "scan_id": S("ScanId"), - "scan_started_at": S("ScanStartedAt"), - "scan_completed_at": S("ScanCompletedAt"), - "trigger_finding_id": S("TriggerFindingId"), - "sources": S("Sources", default=[]), - "scan_detections": S("ScanDetections") >> Bend(AwsGuardDutyScanDetections.mapping), - "scan_type": S("ScanType"), - } - scan_id: Optional[str] = field(default=None, metadata={"description": "Unique Id of the malware scan that generated the finding."}) # fmt: skip - scan_started_at: Optional[datetime] = field(default=None, metadata={"description": "Returns the start date and time of the malware scan."}) # fmt: skip - scan_completed_at: Optional[datetime] = field(default=None, metadata={"description": "Returns the completion date and time of the malware scan."}) # fmt: skip - trigger_finding_id: Optional[str] = field(default=None, metadata={"description": "GuardDuty finding ID that triggered a malware scan."}) # fmt: skip - sources: Optional[List[str]] = field(factory=list, metadata={"description": "Contains list of threat intelligence sources used to detect threats."}) # fmt: skip - scan_detections: Optional[AwsGuardDutyScanDetections] = field(default=None, metadata={"description": "Contains a complete view providing malware scan result details."}) # fmt: skip - scan_type: Optional[str] = field(default=None, metadata={"description": "Specifies the scan type that invoked the malware scan."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyLineageObject: - kind: ClassVar[str] = "aws_guard_duty_lineage_object" - mapping: ClassVar[Dict[str, Bender]] = { - "start_time": S("StartTime"), - "namespace_pid": S("NamespacePid"), - "user_id": S("UserId"), - "name": S("Name"), - "pid": S("Pid"), - "uuid": S("Uuid"), - "executable_path": S("ExecutablePath"), - "euid": S("Euid"), - "parent_uuid": S("ParentUuid"), - } - start_time: Optional[datetime] = field(default=None, metadata={"description": "The time when the process started. This is in UTC format."}) # fmt: skip - namespace_pid: Optional[int] = field(default=None, metadata={"description": "The process ID of the child process."}) # fmt: skip - user_id: Optional[int] = field(default=None, metadata={"description": "The user ID of the user that executed the process."}) # fmt: skip - name: Optional[str] = field(default=None, metadata={"description": "The name of the process."}) # fmt: skip - pid: Optional[int] = field(default=None, metadata={"description": "The ID of the process."}) # fmt: skip - uuid: Optional[str] = field(default=None, metadata={"description": "The unique ID assigned to the process by GuardDuty."}) # fmt: skip - executable_path: Optional[str] = field(default=None, metadata={"description": "The absolute path of the process executable file."}) # fmt: skip - euid: Optional[int] = field(default=None, metadata={"description": "The effective user ID that was used to execute the process."}) # fmt: skip - parent_uuid: Optional[str] = field(default=None, metadata={"description": "The unique ID of the parent process. This ID is assigned to the parent process by GuardDuty."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyProcessDetails: - kind: ClassVar[str] = "aws_guard_duty_process_details" - mapping: ClassVar[Dict[str, Bender]] = { - "name": S("Name"), - "executable_path": S("ExecutablePath"), - "executable_sha256": S("ExecutableSha256"), - "namespace_pid": S("NamespacePid"), - "pwd": S("Pwd"), - "pid": S("Pid"), - "start_time": S("StartTime"), - "uuid": S("Uuid"), - "parent_uuid": S("ParentUuid"), - "user": S("User"), - "user_id": S("UserId"), - "euid": S("Euid"), - "lineage": S("Lineage", default=[]) >> ForallBend(AwsGuardDutyLineageObject.mapping), - } - name: Optional[str] = field(default=None, metadata={"description": "The name of the process."}) # fmt: skip - executable_path: Optional[str] = field(default=None, metadata={"description": "The absolute path of the process executable file."}) # fmt: skip - executable_sha256: Optional[str] = field(default=None, metadata={"description": "The SHA256 hash of the process executable."}) # fmt: skip - namespace_pid: Optional[int] = field(default=None, metadata={"description": "The ID of the child process."}) # fmt: skip - pwd: Optional[str] = field(default=None, metadata={"description": "The present working directory of the process."}) # fmt: skip - pid: Optional[int] = field(default=None, metadata={"description": "The ID of the process."}) # fmt: skip - start_time: Optional[datetime] = field(default=None, metadata={"description": "The time when the process started. This is in UTC format."}) # fmt: skip - uuid: Optional[str] = field(default=None, metadata={"description": "The unique ID assigned to the process by GuardDuty."}) # fmt: skip - parent_uuid: Optional[str] = field(default=None, metadata={"description": "The unique ID of the parent process. This ID is assigned to the parent process by GuardDuty."}) # fmt: skip - user: Optional[str] = field(default=None, metadata={"description": "The user that executed the process."}) # fmt: skip - user_id: Optional[int] = field(default=None, metadata={"description": "The unique ID of the user that executed the process."}) # fmt: skip - euid: Optional[int] = field(default=None, metadata={"description": "The effective user ID of the user that executed the process."}) # fmt: skip - lineage: Optional[List[AwsGuardDutyLineageObject]] = field(factory=list, metadata={"description": "Information about the process's lineage."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyRuntimeContext: - kind: ClassVar[str] = "aws_guard_duty_runtime_context" - mapping: ClassVar[Dict[str, Bender]] = { - "modifying_process": S("ModifyingProcess") >> Bend(AwsGuardDutyProcessDetails.mapping), - "modified_at": S("ModifiedAt"), - "script_path": S("ScriptPath"), - "library_path": S("LibraryPath"), - "ld_preload_value": S("LdPreloadValue"), - "socket_path": S("SocketPath"), - "runc_binary_path": S("RuncBinaryPath"), - "release_agent_path": S("ReleaseAgentPath"), - "mount_source": S("MountSource"), - "mount_target": S("MountTarget"), - "file_system_type": S("FileSystemType"), - "flags": S("Flags", default=[]), - "module_name": S("ModuleName"), - "module_file_path": S("ModuleFilePath"), - "module_sha256": S("ModuleSha256"), - "shell_history_file_path": S("ShellHistoryFilePath"), - "target_process": S("TargetProcess") >> Bend(AwsGuardDutyProcessDetails.mapping), - "address_family": S("AddressFamily"), - "iana_protocol_number": S("IanaProtocolNumber"), - "memory_regions": S("MemoryRegions", default=[]), - "tool_name": S("ToolName"), - "tool_category": S("ToolCategory"), - "service_name": S("ServiceName"), - "command_line_example": S("CommandLineExample"), - "threat_file_path": S("ThreatFilePath"), - } - modifying_process: Optional[AwsGuardDutyProcessDetails] = field(default=None, metadata={"description": "Information about the process that modified the current process. This is available for multiple finding types."}) # fmt: skip - modified_at: Optional[datetime] = field(default=None, metadata={"description": "The timestamp at which the process modified the current process. The timestamp is in UTC date string format."}) # fmt: skip - script_path: Optional[str] = field(default=None, metadata={"description": "The path to the script that was executed."}) # fmt: skip - library_path: Optional[str] = field(default=None, metadata={"description": "The path to the new library that was loaded."}) # fmt: skip - ld_preload_value: Optional[str] = field(default=None, metadata={"description": "The value of the LD_PRELOAD environment variable."}) # fmt: skip - socket_path: Optional[str] = field(default=None, metadata={"description": "The path to the docket socket that was accessed."}) # fmt: skip - runc_binary_path: Optional[str] = field(default=None, metadata={"description": "The path to the leveraged runc implementation."}) # fmt: skip - release_agent_path: Optional[str] = field(default=None, metadata={"description": "The path in the container that modified the release agent file."}) # fmt: skip - mount_source: Optional[str] = field(default=None, metadata={"description": "The path on the host that is mounted by the container."}) # fmt: skip - mount_target: Optional[str] = field(default=None, metadata={"description": "The path in the container that is mapped to the host directory."}) # fmt: skip - file_system_type: Optional[str] = field(default=None, metadata={"description": "Represents the type of mounted fileSystem."}) # fmt: skip - flags: Optional[List[str]] = field(factory=list, metadata={"description": "Represents options that control the behavior of a runtime operation or action. For example, a filesystem mount operation may contain a read-only flag."}) # fmt: skip - module_name: Optional[str] = field(default=None, metadata={"description": "The name of the module loaded into the kernel."}) # fmt: skip - module_file_path: Optional[str] = field(default=None, metadata={"description": "The path to the module loaded into the kernel."}) # fmt: skip - module_sha256: Optional[str] = field(default=None, metadata={"description": "The SHA256 hash of the module."}) # fmt: skip - shell_history_file_path: Optional[str] = field(default=None, metadata={"description": "The path to the modified shell history file."}) # fmt: skip - target_process: Optional[AwsGuardDutyProcessDetails] = field(default=None, metadata={"description": "Information about the process that had its memory overwritten by the current process."}) # fmt: skip - address_family: Optional[str] = field(default=None, metadata={"description": "Represents the communication protocol associated with the address. For example, the address family AF_INET is used for IP version of 4 protocol."}) # fmt: skip - iana_protocol_number: Optional[int] = field(default=None, metadata={"description": "Specifies a particular protocol within the address family. Usually there is a single protocol in address families. For example, the address family AF_INET only has the IP protocol."}) # fmt: skip - memory_regions: Optional[List[str]] = field(factory=list, metadata={"description": "Specifies the Region of a process's address space such as stack and heap."}) # fmt: skip - tool_name: Optional[str] = field(default=None, metadata={"description": "Name of the potentially suspicious tool."}) # fmt: skip - tool_category: Optional[str] = field(default=None, metadata={"description": "Category that the tool belongs to. Some of the examples are Backdoor Tool, Pentest Tool, Network Scanner, and Network Sniffer."}) # fmt: skip - service_name: Optional[str] = field(default=None, metadata={"description": "Name of the security service that has been potentially disabled."}) # fmt: skip - command_line_example: Optional[str] = field(default=None, metadata={"description": "Example of the command line involved in the suspicious activity."}) # fmt: skip - threat_file_path: Optional[str] = field(default=None, metadata={"description": "The suspicious file path for which the threat intelligence details were found."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyRuntimeDetails: - kind: ClassVar[str] = "aws_guard_duty_runtime_details" - mapping: ClassVar[Dict[str, Bender]] = { - "process": S("Process") >> Bend(AwsGuardDutyProcessDetails.mapping), - "context": S("Context") >> Bend(AwsGuardDutyRuntimeContext.mapping), - } - process: Optional[AwsGuardDutyProcessDetails] = field(default=None, metadata={"description": "Information about the observed process."}) # fmt: skip - context: Optional[AwsGuardDutyRuntimeContext] = field(default=None, metadata={"description": "Additional information about the suspicious activity."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyAnomalyUnusual: - kind: ClassVar[str] = "aws_guard_duty_anomaly_unusual" - mapping: ClassVar[Dict[str, Bender]] = {"behavior": S("Behavior")} - behavior: Optional[Dict[str, Any]] = field(default=None, metadata={"description": "The behavior of the anomalous activity that caused GuardDuty to generate the finding."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyAnomaly: - kind: ClassVar[str] = "aws_guard_duty_anomaly" - mapping: ClassVar[Dict[str, Bender]] = { - "profiles": S("Profiles"), - "unusual": S("Unusual") >> Bend(AwsGuardDutyAnomalyUnusual.mapping), - } - profiles: Optional[Dict[str, Any]] = field(default=None, metadata={"description": "Information about the types of profiles."}) # fmt: skip - unusual: Optional[AwsGuardDutyAnomalyUnusual] = field(default=None, metadata={"description": "Information about the behavior of the anomalies."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyDetection: - kind: ClassVar[str] = "aws_guard_duty_detection" - mapping: ClassVar[Dict[str, Bender]] = {"anomaly": S("Anomaly") >> Bend(AwsGuardDutyAnomaly.mapping)} - anomaly: Optional[AwsGuardDutyAnomaly] = field(default=None, metadata={"description": "The details about the anomalous activity that caused GuardDuty to generate the finding."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyItemPath: - kind: ClassVar[str] = "aws_guard_duty_item_path" - mapping: ClassVar[Dict[str, Bender]] = {"nested_item_path": S("NestedItemPath"), "hash": S("Hash")} - nested_item_path: Optional[str] = field(default=None, metadata={"description": "The nested item path where the infected file was found."}) # fmt: skip - hash: Optional[str] = field(default=None, metadata={"description": "The hash value of the infected resource."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyThreat: - kind: ClassVar[str] = "aws_guard_duty_threat" - mapping: ClassVar[Dict[str, Bender]] = { - "name": S("Name"), - "source": S("Source"), - "item_paths": S("ItemPaths", default=[]) >> ForallBend(AwsGuardDutyItemPath.mapping), - } - name: Optional[str] = field(default=None, metadata={"description": "Name of the detected threat that caused GuardDuty to generate this finding."}) # fmt: skip - source: Optional[str] = field(default=None, metadata={"description": "Source of the threat that generated this finding."}) # fmt: skip - item_paths: Optional[List[AwsGuardDutyItemPath]] = field(factory=list, metadata={"description": "Information about the nested item path and hash of the protected resource."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyMalwareScanDetails: - kind: ClassVar[str] = "aws_guard_duty_malware_scan_details" - mapping: ClassVar[Dict[str, Bender]] = { - "threats": S("Threats", default=[]) >> ForallBend(AwsGuardDutyThreat.mapping) - } - threats: Optional[List[AwsGuardDutyThreat]] = field(factory=list, metadata={"description": "Information about the detected threats associated with the generated GuardDuty finding."}) # fmt: skip - - -@define(eq=False, slots=False) -class AwsGuardDutyService: - kind: ClassVar[str] = "aws_guard_duty_service" - mapping: ClassVar[Dict[str, Bender]] = { - "action": S("Action") >> Bend(AwsGuardDutyAction.mapping), - "evidence": S("Evidence") >> Bend(AwsGuardDutyEvidence.mapping), - "archived": S("Archived"), - "count": S("Count"), - "detector_id": S("DetectorId"), - "event_first_seen": S("EventFirstSeen"), - "event_last_seen": S("EventLastSeen"), - "resource_role": S("ResourceRole"), - "service_name": S("ServiceName"), - "user_feedback": S("UserFeedback"), - "additional_info": S("AdditionalInfo") >> Bend(AwsGuardDutyServiceAdditionalInfo.mapping), - "feature_name": S("FeatureName"), - "ebs_volume_scan_details": S("EbsVolumeScanDetails") >> Bend(AwsGuardDutyEbsVolumeScanDetails.mapping), - "runtime_details": S("RuntimeDetails") >> Bend(AwsGuardDutyRuntimeDetails.mapping), - "detection": S("Detection") >> Bend(AwsGuardDutyDetection.mapping), - "malware_scan_details": S("MalwareScanDetails") >> Bend(AwsGuardDutyMalwareScanDetails.mapping), - } - action: Optional[AwsGuardDutyAction] = field(default=None, metadata={"description": "Information about the activity that is described in a finding."}) # fmt: skip - evidence: Optional[AwsGuardDutyEvidence] = field(default=None, metadata={"description": "An evidence object associated with the service."}) # fmt: skip - archived: Optional[bool] = field(default=None, metadata={"description": "Indicates whether this finding is archived."}) # fmt: skip - count: Optional[int] = field(default=None, metadata={"description": "The total count of the occurrences of this finding type."}) # fmt: skip - detector_id: Optional[str] = field(default=None, metadata={"description": "The detector ID for the GuardDuty service."}) # fmt: skip - event_first_seen: Optional[str] = field(default=None, metadata={"description": "The first-seen timestamp of the activity that prompted GuardDuty to generate this finding."}) # fmt: skip - event_last_seen: Optional[str] = field(default=None, metadata={"description": "The last-seen timestamp of the activity that prompted GuardDuty to generate this finding."}) # fmt: skip - resource_role: Optional[str] = field(default=None, metadata={"description": "The resource role information for this finding."}) # fmt: skip - service_name: Optional[str] = field(default=None, metadata={"description": "The name of the Amazon Web Services service (GuardDuty) that generated a finding."}) # fmt: skip - user_feedback: Optional[str] = field(default=None, metadata={"description": "Feedback that was submitted about the finding."}) # fmt: skip - additional_info: Optional[AwsGuardDutyServiceAdditionalInfo] = field(default=None, metadata={"description": "Contains additional information about the generated finding."}) # fmt: skip - feature_name: Optional[str] = field(default=None, metadata={"description": "The name of the feature that generated a finding."}) # fmt: skip - ebs_volume_scan_details: Optional[AwsGuardDutyEbsVolumeScanDetails] = field(default=None, metadata={"description": "Returns details from the malware scan that created a finding."}) # fmt: skip - runtime_details: Optional[AwsGuardDutyRuntimeDetails] = field(default=None, metadata={"description": "Information about the process and any required context values for a specific finding"}) # fmt: skip - detection: Optional[AwsGuardDutyDetection] = field(default=None, metadata={"description": "Contains information about the detected unusual behavior."}) # fmt: skip - malware_scan_details: Optional[AwsGuardDutyMalwareScanDetails] = field(default=None, metadata={"description": "Returns details from the malware scan that generated a GuardDuty finding."}) # fmt: skip - - @define(eq=False, slots=False) class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): kind: ClassVar[str] = "aws_guard_duty_finding" - _kind_display: ClassVar[str] = "AWS GuardDuty Finding" - _kind_description: ClassVar[str] = ( - "AWS GuardDuty Finding represents a potential security issue identified by Amazon GuardDuty. " - "GuardDuty uses machine learning, anomaly detection, and integrated threat intelligence to detect and " - "alert on suspicious activity in your AWS environment. Findings highlight possible attacks or vulnerabilities " - "that may require further investigation." - ) - _kind_service: ClassVar[Optional[str]] = service_name - _metadata: ClassVar[Dict[str, Any]] = {"icon": "log", "group": "management"} - _docs_url: ClassVar[str] = "https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings.html" - _aws_metadata: ClassVar[Dict[str, Any]] = { - "provider_link_tpl": "https://{region_id}.console.aws.amazon.com/guardduty/home?region={region_id}#/findings?fId={id}¯os=current", - } + _model_export: ClassVar[bool] = False # do not export this class, since there will be no instances of it # api spec defined in `collect_resources` mapping: ClassVar[Dict[str, Bender]] = { "id": S("Id"), @@ -1239,10 +593,11 @@ class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): "finding_region": S("Region"), "finding_resource": S("Resource") >> Bend(AwsGuardDutyResource.mapping), "schema_version": S("SchemaVersion"), - "finding_service": S("Service") >> Bend(AwsGuardDutyService.mapping), "finding_severity": S("Severity"), "title": S("Title"), "type": S("Type"), + # available but not used property: + # "finding_service": S("Service"), } account_id: Optional[str] = field(default=None, metadata={"description": "The ID of the account in which the finding was generated."}) # fmt: skip confidence: Optional[float] = field(default=None, metadata={"description": "The confidence score for the finding."}) # fmt: skip @@ -1251,7 +606,6 @@ class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): finding_region: Optional[str] = field(default=None, metadata={"description": "The Region where the finding was generated."}) # fmt: skip finding_resource: Optional[AwsGuardDutyResource] = field(default=None, metadata={"description": "Contains information about the Amazon Web Services resource associated with the activity that prompted GuardDuty to generate a finding."}) # fmt: skip schema_version: Optional[str] = field(default=None, metadata={"description": "The version of the schema used for the finding."}) # fmt: skip - finding_service: Optional[AwsGuardDutyService] = field(default=None, metadata={"description": "Contains additional information about the generated finding."}) # fmt: skip finding_severity: Optional[float] = field(default=None, metadata={"description": "The severity of the finding."}) # fmt: skip title: Optional[str] = field(default=None, metadata={"description": "The title of the finding."}) # fmt: skip type: Optional[str] = field(default=None, metadata={"description": "The type of finding."}) # fmt: skip @@ -1260,30 +614,6 @@ class AwsGuardDutyFinding(AwsResource, PhantomBaseResource): def service_name(cls) -> str: return service_name - @staticmethod - def set_findings(builder: GraphBuilder, resource_to_set: AwsResource, to_check: str = "id") -> None: - """ - Set the assessment findings for the resource based on its ID or ARN. - """ - id_or_arn_or_name = "" - - if to_check == "arn": - if not resource_to_set.arn: - return - id_or_arn_or_name = resource_to_set.arn - elif to_check == "id": - id_or_arn_or_name = resource_to_set.id - elif to_check == "name": - id_or_arn_or_name = resource_to_set.safe_name - else: - return - provider_findings = builder._assessment_findings.get( - AssessmentKey("guard_duty", resource_to_set.region().id, resource_to_set.__class__.__name__), {} - ).get(id_or_arn_or_name, []) - if provider_findings: - # Set the findings in the resource's _assessments dictionary - resource_to_set._assessments.append(Assessment("guard_duty", provider_findings)) - def parse_finding(self, source: Json) -> Finding: def get_severity() -> Severity: if not self.finding_severity: @@ -1300,10 +630,7 @@ def get_severity() -> Severity: return Severity.critical finding_title = self.safe_name - if not self.finding_severity: - finding_severity = Severity.medium - else: - finding_severity = get_severity() + finding_severity = get_severity() description = self.description updated_at = self.mtime details = source.get("Service", {}) @@ -1314,54 +641,53 @@ def collect_resources(cls: Type[AwsResource], builder: GraphBuilder) -> None: def check_type_and_adjust_id( finding_resource: AwsGuardDutyResource, - ) -> List[Tuple[str, str]]: - # To avoid circular imports, defined here - from fix_plugin_aws.resource.ec2 import AwsEc2Instance, AwsEc2Volume - from fix_plugin_aws.resource.ecs import AwsEcsCluster - from fix_plugin_aws.resource.eks import AwsEksCluster - from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction - from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance - from fix_plugin_aws.resource.s3 import AwsS3Bucket - - finding_resources = [] + ) -> List[Tuple[Type[Any], Dict[str, Any]]]: + + finding_resources: List[Tuple[Type[AwsResource], Dict[str, Any]]] = [] if finding_resource.s3_bucket_details: for s3_bucket_detail in finding_resource.s3_bucket_details: if s3_bucket_detail.name: - finding_resources.append((AwsS3Bucket.__name__, s3_bucket_detail.name)) + finding_resources.append((AwsS3Bucket, {"name": s3_bucket_detail.name})) if finding_resource.instance_details and finding_resource.instance_details.instance_id: - finding_resources.append((AwsEc2Instance.__name__, finding_resource.instance_details.instance_id)) + finding_resources.append((AwsEc2Instance, {"id": finding_resource.instance_details.instance_id})) if finding_resource.eks_cluster_details and finding_resource.eks_cluster_details.arn: - finding_resources.append((AwsEksCluster.__name__, finding_resource.eks_cluster_details.arn)) + finding_resources.append((AwsEksCluster, {"arn": finding_resource.eks_cluster_details.arn})) if finding_resource.ebs_volume_details: for vol_detail in finding_resource.ebs_volume_details.scanned_volume_details or []: if vol_detail.volume_arn: - finding_resources.append((AwsEc2Volume.__name__, vol_detail.volume_arn)) + finding_resources.append((AwsEc2Volume, {"arn": vol_detail.volume_arn})) for vol_detail in finding_resource.ebs_volume_details.skipped_volume_details or []: if vol_detail.volume_arn: - finding_resources.append((AwsEc2Volume.__name__, vol_detail.volume_arn)) + finding_resources.append((AwsEc2Volume, {"arn": vol_detail.volume_arn})) if finding_resource.ecs_cluster_details and finding_resource.ecs_cluster_details.arn: - finding_resources.append((AwsEcsCluster.__name__, finding_resource.ecs_cluster_details.arn)) + finding_resources.append((AwsEcsCluster, {"arn": finding_resource.ecs_cluster_details.arn})) if finding_resource.rds_db_instance_details: if finding_resource.rds_db_instance_details.db_instance_identifier: finding_resources.append( - (AwsRdsInstance.__name__, finding_resource.rds_db_instance_details.db_instance_identifier) + (AwsRdsInstance, {"id": finding_resource.rds_db_instance_details.db_instance_identifier}) ) if finding_resource.rds_db_instance_details.db_cluster_identifier: finding_resources.append( - (AwsRdsCluster.__name__, finding_resource.rds_db_instance_details.db_cluster_identifier) + (AwsRdsCluster, {"id": finding_resource.rds_db_instance_details.db_cluster_identifier}) ) if finding_resource.lambda_details and finding_resource.lambda_details.function_name: - finding_resources.append((AwsLambdaFunction.__name__, finding_resource.lambda_details.function_name)) + finding_resources.append((AwsLambdaFunction, {"name": finding_resource.lambda_details.function_name})) return finding_resources + def add_finding( + provider: str, finding: Finding, clazz: Optional[Type[AwsResource]] = None, **node: Any + ) -> None: + if resource := builder.node(clazz=clazz, **node): + resource.add_finding(provider, finding) + try: detector_ids = builder.client.list(service_name, "list-detectors", "DetectorIds") finding_id_futures = { @@ -1401,14 +727,15 @@ def check_type_and_adjust_id( if instance := AwsGuardDutyFinding.from_api(finding, builder): if fr := instance.finding_resource: found_info = check_type_and_adjust_id(fr) - for class_name, id_or_arn_or_name in found_info: - adjusted_finding = instance.parse_finding(finding) - builder.add_finding( - "guard_duty", - class_name, - instance.finding_region or "global", - id_or_arn_or_name, - adjusted_finding, + for clazz, res_filter in found_info: + builder.after_collect_actions.append( + partial( + add_finding, + amazon_guardduty, + instance.parse_finding(finding), + clazz, + **res_filter, + ) ) except Boto3Error as e: msg = f"Error while collecting {cls.__name__} in region {builder.region.name}: {e}" diff --git a/plugins/aws/fix_plugin_aws/resource/lambda_.py b/plugins/aws/fix_plugin_aws/resource/lambda_.py index c435615942..599bef09ce 100644 --- a/plugins/aws/fix_plugin_aws/resource/lambda_.py +++ b/plugins/aws/fix_plugin_aws/resource/lambda_.py @@ -10,7 +10,6 @@ from fix_plugin_aws.resource.base import AwsResource, GraphBuilder, AwsApiSpec, parse_json from fix_plugin_aws.resource.cloudwatch import AwsCloudwatchQuery, normalizer_factory from fix_plugin_aws.resource.ec2 import AwsEc2Subnet, AwsEc2SecurityGroup, AwsEc2Vpc -from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.kms import AwsKmsKey from fixlib.baseresources import ( BaseServerlessFunction, @@ -406,7 +405,6 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: clazz=AwsKmsKey, arn=self.function_kms_key_arn, ) - AwsGuardDutyFinding.set_findings(builder, self, "name") def update_resource_tag(self, client: AwsClient, key: str, value: str) -> bool: client.call( diff --git a/plugins/aws/fix_plugin_aws/resource/rds.py b/plugins/aws/fix_plugin_aws/resource/rds.py index f6d644a02b..c1427d463a 100644 --- a/plugins/aws/fix_plugin_aws/resource/rds.py +++ b/plugins/aws/fix_plugin_aws/resource/rds.py @@ -7,7 +7,6 @@ from fix_plugin_aws.resource.base import AwsApiSpec, AwsResource, GraphBuilder from fix_plugin_aws.resource.cloudwatch import AwsCloudwatchQuery, AwsCloudwatchMetricData, normalizer_factory from fix_plugin_aws.resource.ec2 import AwsEc2SecurityGroup, AwsEc2Subnet, AwsEc2Vpc -from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.kinesis import AwsKinesisStream from fix_plugin_aws.resource.kms import AwsKmsKey from fix_plugin_aws.utils import ToDict, TagsValue @@ -599,8 +598,6 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: for key_reference in keys: builder.dependant_node(from_node=self, clazz=AwsKmsKey, id=AwsKmsKey.normalise_id(key_reference)) - AwsGuardDutyFinding.set_findings(builder, self, "id") - def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call( aws_service=self.api_spec.service, @@ -1042,7 +1039,6 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: ) if kinesis := self.rds_activity_stream_kinesis_stream_name: builder.add_edge(self, clazz=AwsKinesisStream, name=kinesis) - AwsGuardDutyFinding.set_findings(builder, self, "id") def delete_resource(self, client: AwsClient, graph: Graph) -> bool: client.call( diff --git a/plugins/aws/fix_plugin_aws/resource/s3.py b/plugins/aws/fix_plugin_aws/resource/s3.py index db54ccec16..bb52c3b8f9 100644 --- a/plugins/aws/fix_plugin_aws/resource/s3.py +++ b/plugins/aws/fix_plugin_aws/resource/s3.py @@ -11,7 +11,6 @@ from fix_plugin_aws.aws_client import AwsClient from fix_plugin_aws.resource.base import AwsResource, AwsApiSpec, GraphBuilder, parse_json from fix_plugin_aws.resource.cloudwatch import AwsCloudwatchQuery, normalizer_factory -from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.utils import tags_as_dict from fixlib.baseresources import ( BaseBucket, @@ -180,7 +179,6 @@ class AwsS3Bucket(AwsResource, BaseBucket, HasResourcePolicy): _kind_description: ClassVar[str] = "AWS S3 Bucket is a cloud storage service provided by Amazon Web Services. It stores and retrieves data objects, such as files, documents, and images. S3 Buckets organize data into containers, offering features like access control, versioning, and lifecycle management. Users can interact with S3 Buckets through APIs, SDKs, or the AWS Management Console." # fmt: skip _docs_url: ClassVar[str] = "https://docs.aws.amazon.com/AmazonS3/latest/userguide/creating-bucket.html" _kind_service: ClassVar[Optional[str]] = service_name - _reference_kinds: ClassVar[ModelReference] = {} api_spec: ClassVar[AwsApiSpec] = AwsApiSpec( service_name, "list-buckets", "Buckets", override_iam_permission="s3:ListAllMyBuckets" ) @@ -216,9 +214,6 @@ def called_collect_apis(cls) -> List[AwsApiSpec]: AwsApiSpec(service_name, "get-bucket-lifecycle-configuration"), ] - def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: - AwsGuardDutyFinding.set_findings(builder, self, "name") - @classmethod def collect(cls: Type[AwsResource], json: List[Json], builder: GraphBuilder) -> None: def add_tags(bucket: AwsS3Bucket) -> None: diff --git a/plugins/aws/test/collector_test.py b/plugins/aws/test/collector_test.py index 6254df5249..9de124602e 100644 --- a/plugins/aws/test/collector_test.py +++ b/plugins/aws/test/collector_test.py @@ -11,7 +11,6 @@ called_collect_apis, called_mutator_apis, ) -from fix_plugin_aws.resource.guardduty import AwsGuardDutyFinding from fix_plugin_aws.resource.base import AwsResource, AwsApiSpec, GraphBuilder, AwsRegion from fix_plugin_aws.resource.ec2 import AwsEc2Instance from fixlib.baseresources import BaseResource From 27952973bbdb5e7fdbb7c858e1cda08b2a40325d Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 31 Oct 2024 15:26:37 +0000 Subject: [PATCH 16/18] feat: fixed mypy --- plugins/aws/fix_plugin_aws/resource/base.py | 4 ---- plugins/aws/fix_plugin_aws/resource/ecr.py | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/aws/fix_plugin_aws/resource/base.py b/plugins/aws/fix_plugin_aws/resource/base.py index 7a487b24c7..3441050d09 100644 --- a/plugins/aws/fix_plugin_aws/resource/base.py +++ b/plugins/aws/fix_plugin_aws/resource/base.py @@ -1,6 +1,5 @@ from __future__ import annotations -from collections import defaultdict import logging import re from abc import ABC @@ -503,9 +502,6 @@ def __init__( def suppress(self, message: str) -> SuppressWithFeedback: return SuppressWithFeedback(message, self.core_feedback, log) - def add_finding(self, provider: str, class_name: str, region: str, class_id: str, finding: Finding) -> None: - self._assessment_findings[AssessmentKey(provider, region, class_name)][class_id].append(finding) - def submit_work(self, service: str, fn: Callable[..., T], *args: Any, **kwargs: Any) -> Future[T]: """ Use this method for work that can be done in parallel. diff --git a/plugins/aws/fix_plugin_aws/resource/ecr.py b/plugins/aws/fix_plugin_aws/resource/ecr.py index 592f1f26a7..2bf480ca1e 100644 --- a/plugins/aws/fix_plugin_aws/resource/ecr.py +++ b/plugins/aws/fix_plugin_aws/resource/ecr.py @@ -8,7 +8,7 @@ from fix_plugin_aws.resource.base import AwsResource, AwsApiSpec, GraphBuilder from fix_plugin_aws.utils import ToDict -from fixlib.baseresources import HasResourcePolicy, ModelReference, PolicySource, PolicySourceKind +from fixlib.baseresources import HasResourcePolicy, PolicySource, PolicySourceKind from fixlib.json import sort_json from fixlib.json_bender import Bender, S, Bend from fixlib.types import Json From 8a1773958334f3c83f88d71ada4b691119c3dda7 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 31 Oct 2024 16:02:44 +0000 Subject: [PATCH 17/18] fixed linter --- plugins/aws/fix_plugin_aws/resource/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/aws/fix_plugin_aws/resource/base.py b/plugins/aws/fix_plugin_aws/resource/base.py index 3441050d09..75a7158f73 100644 --- a/plugins/aws/fix_plugin_aws/resource/base.py +++ b/plugins/aws/fix_plugin_aws/resource/base.py @@ -27,7 +27,6 @@ BaseVolumeType, Cloud, EdgeType, - Finding, ModelReference, PhantomBaseResource, BaseOrganizationalRoot, From ecfd78ae201d280e5f7e0a954447bfe152671b19 Mon Sep 17 00:00:00 2001 From: Kirill Date: Fri, 1 Nov 2024 10:22:53 +0000 Subject: [PATCH 18/18] feat: added aws filter while fetching findings --- .../aws/fix_plugin_aws/resource/guardduty.py | 26 +++++++++---------- ..._foo.json => list-findings__foo_test.json} | 0 2 files changed, 13 insertions(+), 13 deletions(-) rename plugins/aws/test/resources/files/guardduty/{list-findings__foo.json => list-findings__foo_test.json} (100%) diff --git a/plugins/aws/fix_plugin_aws/resource/guardduty.py b/plugins/aws/fix_plugin_aws/resource/guardduty.py index 4e333f0cba..14b4e89492 100644 --- a/plugins/aws/fix_plugin_aws/resource/guardduty.py +++ b/plugins/aws/fix_plugin_aws/resource/guardduty.py @@ -699,6 +699,7 @@ def add_finding( "FindingIds", expected_errors=["BadRequestException"], DetectorId=detector_id, + FindingCriteria={"Criterion": {"accountId": {"Eq": [builder.account.id]}}}, ): detector_id for detector_id in detector_ids } @@ -723,20 +724,19 @@ def add_finding( for chunk_future in as_completed(chunk_futures): findings = chunk_future.result() for finding in findings: - if finding.get("AccountId", None) == builder.account.id: - if instance := AwsGuardDutyFinding.from_api(finding, builder): - if fr := instance.finding_resource: - found_info = check_type_and_adjust_id(fr) - for clazz, res_filter in found_info: - builder.after_collect_actions.append( - partial( - add_finding, - amazon_guardduty, - instance.parse_finding(finding), - clazz, - **res_filter, - ) + if instance := AwsGuardDutyFinding.from_api(finding, builder): + if fr := instance.finding_resource: + found_info = check_type_and_adjust_id(fr) + for clazz, res_filter in found_info: + builder.after_collect_actions.append( + partial( + add_finding, + amazon_guardduty, + instance.parse_finding(finding), + clazz, + **res_filter, ) + ) except Boto3Error as e: msg = f"Error while collecting {cls.__name__} in region {builder.region.name}: {e}" builder.core_feedback.error(msg, log) diff --git a/plugins/aws/test/resources/files/guardduty/list-findings__foo.json b/plugins/aws/test/resources/files/guardduty/list-findings__foo_test.json similarity index 100% rename from plugins/aws/test/resources/files/guardduty/list-findings__foo.json rename to plugins/aws/test/resources/files/guardduty/list-findings__foo_test.json