Compare commits

..

211 Commits

Author SHA1 Message Date
pedrooot ac9bc2c916 Merge branch 'master' into prowler-inventory 2024-10-09 17:09:19 +02:00
Mario Rodriguez Lopez 16191a7b15 feat(elasticbeanstalk): Add new service ElasticBeanstalk (#5322)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-09 09:29:19 -04:00
Rubén De la Torre Vico 0c149461b3 chore(sns): manage ResourceNotFoundException and add paralelism (#5345) 2024-10-09 08:56:39 -04:00
Pedro Martín 3ee39cff2a feat(scan): execute all checks if no checks are provided (#5307) 2024-10-09 11:46:38 +02:00
Pedro Martín 41ba118cc4 feat(scan): add scan duration (#5305)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com>
Co-authored-by: Sergio <sergio@prowler.com>
Co-authored-by: Prowler Bot <bot@prowler.com>
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
Co-authored-by: Rubén De la Torre Vico <rubendltv22@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
Co-authored-by: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com>
2024-10-09 11:12:39 +02:00
Sergio Garcia e0587fe0cf fix(Dockerfile): install git dependency (#5339) 2024-10-09 08:58:55 +02:00
Daniel Barranquero 50481665ce feat(redshift): add new check redshift_cluster_in_transit_encryption_enabled (#5271)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-10-08 14:15:32 -04:00
Prowler Bot a49c744e08 chore(regions_update): Changes in regions for AWS services (#5323)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-08 14:13:17 -04:00
Rubén De la Torre Vico aa32634105 chore(guardduty): mock failing tests using moto (#5334) 2024-10-08 13:27:37 -04:00
Rubén De la Torre Vico b27898de1d chore(ecs): mock all tests using moto (#5326) 2024-10-08 12:11:33 -04:00
Sergio Garcia b703357027 chore(secrets): use master branch of Yelp/detect-secrets (#5298)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
2024-10-08 09:55:46 -04:00
Rubén De la Torre Vico 27cd9b22df feat(guardduty): add new check guardduty_lambda_protection_enabled (#5299)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-08 08:20:23 -04:00
Pepe Fagoaga 5bf85366e0 chore(secrets): Add TelegramBotToken detector (#5321) 2024-10-08 08:09:26 -04:00
dependabot[bot] 30bc971f4b chore(deps): bump trufflesecurity/trufflehog from 3.82.6 to 3.82.7 (#5315)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-08 10:44:56 +02:00
Sergio Garcia 3950d7eba8 fix(threat detection): ignore AWS services events (#5276) 2024-10-07 14:25:09 -04:00
Rubén De la Torre Vico 2f8a3d2ef8 feat(guardduty): add new check guardduty_ec2_malware_protection_enabled (#5297)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-07 13:03:36 -04:00
Prowler Bot 3b64bbd3a8 chore(regions_update): Changes in regions for AWS services (#5302)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-10-07 10:58:40 -04:00
Hugo Pereira Brito 09d099891a feat(wafv2): change web_acls from list to dict (#5308)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-07 10:23:58 -04:00
Mario Rodriguez Lopez a6b10a8611 feat(efs): add new check efs_access_point_enforce_user_identity (#5285)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-10-04 15:16:10 -04:00
Lefteris c239ede3f9 feat(glue): add check glue_ml_transform_encrypted_at_rest (#5272)
Co-authored-by: Lefteris Gilmaz <lefterisgilmaz@Lefteriss-MacBook-Pro.local>
Co-authored-by: Rubén De la Torre Vico <rubendltv22@gmail.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-04 14:13:11 -04:00
Hugo Pereira Brito 66f2754017 feat(networkfirewall): add new check networkfirewall_policy_default_action_full_packets (#5284)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-10-04 14:00:25 -04:00
Hugo Pereira Brito 9138ecdce9 feat(kinesis): add new check kinesis_stream_encrypted_at_rest (#5292)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-10-04 13:59:49 -04:00
Rubén De la Torre Vico 2b66368cf2 feat(guardduty): add new check guardduty_eks_audit_log_enabled (#5293)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-04 13:43:04 -04:00
Mario Rodriguez Lopez aa3425a7de feat(efs): add new check efs_access_point_enforce_root_directory (#5277)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-10-04 13:12:47 -04:00
Mario Rodriguez Lopez a31b15c26c feat(efs): add new check efs_mount_target_not_publicly_accesible (#5275)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-04 11:41:51 -04:00
Hugo Pereira Brito f2301d5ed6 feat(networkfirewall): add new check networkfirewall_policy_default_action_fragmented_packets (#5244)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-10-04 11:41:26 -04:00
Rubén De la Torre Vico df10253056 chore(cloudwatch): Improve checks related with function check_cloudwatch_log_metric_filter (#5286) 2024-10-04 11:18:46 -04:00
Sergio Garcia d5acdc766a chore(ocsf): adapt mapping for version 1.3.0 (#5287) 2024-10-04 10:59:51 -04:00
Rubén De la Torre Vico e389e0136f chore(cloudwatch): add tags to missing checks report (#5261)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-03 18:04:43 -04:00
Rubén De la Torre Vico 8bb3bd0dcb chore(iam): add tags to missing checks report (#5280)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-03 13:47:10 -04:00
Hugo Pereira Brito 4d4bf3fa11 feat(networkfirewall): add new check networkfirewall_multi_az (#5247)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-03 13:46:44 -04:00
Daniel Barranquero e99c58405c feat(redshift): add new check redshift_cluster_non_default_database_name (#5283)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-03 11:28:54 -04:00
pedrooot d437d1c4b8 feat(inventory): change name and behaviour 2024-10-03 11:57:17 +02:00
pedrooot 2cf1f22235 feat(scan-inventory): take back changes from quick inventory 2024-10-02 15:45:01 -06:00
Daniel Barranquero 2177704b4b feat(redshift): add new check redshift_cluster_encrypted_at_rest (#5262)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-02 17:06:19 -04:00
Mario Rodriguez Lopez 2ffe7f3ef7 feat(ecs): add new check ecs_service_fargate_latest_platform_version (#5258)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-02 16:50:20 -04:00
dependabot[bot] 158263a8bf chore(deps-dev): bump moto from 5.0.15 to 5.0.16 (#5256)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-02 15:40:34 -04:00
Daniel Barranquero 469986dd28 feat(redshift): add new check redshift_cluster_non_default_username (#5268) 2024-10-02 13:54:12 -04:00
Hugo Pereira Brito ff101087bf feat(networkfirewall): add new check networkfirewall_logging_enabled (#5145)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-10-02 12:09:13 -04:00
dependabot[bot] b2151e2e9c chore(deps): bump boto3 from 1.35.28 to 1.35.29 (#5257)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-02 11:27:39 -04:00
Sergio Garcia 2c4244b1fb chore(version): update Prowler version (#5251) 2024-10-02 11:14:26 -04:00
Hugo Pereira Brito 260cdf575a feat(kinesis): add new service Kinesis (#5228)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-10-02 10:59:59 -04:00
Michael St.Onge ab4190c215 chore(contrib): update aws-multi-account-securityhub deployment (#5263) 2024-10-02 10:58:02 -04:00
pedrooot 3ef1d41630 feat(inventory): name on output folder 2024-10-02 08:46:42 -06:00
Mario Rodriguez Lopez 7f97b0a57f feat(ecs): Ensure ECS clusters use Container Insights (#5241)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-02 10:42:52 -04:00
Daniel Barranquero 2c2dd82d0c feat(dynamodb): add new check dynamodb_table_autoscaling_enabled (#5129)
Co-authored-by: Sergio <sergio@prowler.com>
2024-10-02 10:42:36 -04:00
Mario Rodriguez Lopez 2511df1732 fix(ecs): Adjust code to the new ARN formats in the ECS service (#5259) 2024-10-02 09:40:32 -04:00
Rubén De la Torre Vico f955dd76d9 test(aws): fix failing tests for ecs_task_definitions_logging_enabled and ssm_managed_compliant_patching (#5267) 2024-10-02 09:35:27 -04:00
Prowler Bot a08cc769c8 chore(regions_update): Changes in regions for AWS services (#5269)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-10-02 08:50:30 -04:00
pedrooot e7d719e514 feat(inventory): change prints for inventory 2024-10-01 14:18:44 -06:00
pedrooot 2b51cf8fca feat(inventory): rename inventory to scan-inventory 2024-10-01 13:57:52 -06:00
pedrooot c5929efc99 feat(inventory): add prowler inventory for aws 2024-10-01 13:47:39 -06:00
Prowler Bot 77ac5e3b91 chore(regions_update): Changes in regions for AWS services (#5260)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-10-01 14:10:38 -04:00
dependabot[bot] 2da8f2b1eb chore(deps-dev): bump mkdocs-material from 9.5.38 to 9.5.39 (#5255)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 14:10:17 -04:00
Sergio Garcia 38e024216c chore(ec2): enhance security group with any open port check (#5215) 2024-09-30 14:53:04 -04:00
Rubén De la Torre Vico 8e4847ec89 fix(rds): add comprobations before list tags (#5249)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-30 13:34:22 -04:00
Sergio Garcia c6d34e8089 chore(README): update summary table (#5248) 2024-09-30 12:56:42 -04:00
Hugo Pereira Brito 880523076d feat(networkfirewall): add new check networkfirewall_policy_rule_group_associated (#5225) 2024-09-30 12:04:32 -04:00
Sergio Garcia 3d2f1a3aa7 fix(aws): handle none type attributes (#5216) 2024-09-30 18:04:14 +02:00
Rubén De la Torre Vico c9ff96144d chore(ssm): add tags to ssm_managed_compliant_patching (#5245)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-30 12:00:43 -04:00
johannes-engler-mw 234f8c2958 feat(azure containerregistry): gather service infos and checks disabled admin user (#5191)
Co-authored-by: Pedro Martín <pedromarting3@gmail.com>
Co-authored-by: Rubén De la Torre Vico <rubendltv22@gmail.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-30 11:52:48 -04:00
Sergio Garcia da87c0d81e fix(tests): patch head_bucket function correctly (#5246) 2024-09-30 11:00:30 -04:00
dependabot[bot] 7732ec7d34 chore(deps-dev): bump safety from 3.2.7 to 3.2.8 (#5238)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 10:54:36 -04:00
Rubén De la Torre Vico a1b9b2171f feat(securityhub): add tags securityhub_enabled (#5231)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-30 10:13:41 -04:00
Mario Rodriguez Lopez 30e3fd9e46 feat(ecs): Ensure ECS containers have a logging configuration specified (#5234)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-30 09:43:20 -04:00
dependabot[bot] 3db541a42a chore(deps): bump botocore from 1.35.28 to 1.35.29 (#5239)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 09:38:12 -04:00
Rubén De la Torre Vico d5abe16180 feat(wafv2): add tags to wafv2_webacl_logging_enabled (#5243) 2024-09-30 09:37:16 -04:00
dependabot[bot] 564b18c388 chore(deps): bump azure-storage-blob from 12.23.0 to 12.23.1 (#5240)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-30 08:30:28 -04:00
Rubén De la Torre Vico 13e40eb03e feat(aws): add tags to Global Accelerator (#5233) 2024-09-27 12:37:19 -04:00
Rubén De la Torre Vico b402ced402 docs: change installation methods (#5192) 2024-09-27 12:15:14 -04:00
dependabot[bot] 6bbb9d04a6 chore(deps): bump boto3 from 1.35.26 to 1.35.28 (#5232)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 12:13:56 -04:00
dependabot[bot] 6616657c91 chore(deps): bump botocore from 1.35.27 to 1.35.28 (#5220)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 11:30:21 -04:00
Amogh Bantwal 853b833cfb feat(aws): Add new check opensearch_service_domains_access_control_enabled (#5203)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-27 10:13:43 -04:00
Rubén De la Torre Vico c047b29140 feat(rds): add missing tags to RDS checks (#5230) 2024-09-27 09:34:25 -04:00
Prowler Bot c4a39662ae chore(regions_update): Changes in regions for AWS services (#5224)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-27 12:30:05 +02:00
dependabot[bot] 66e804f212 chore(deps): bump trufflesecurity/trufflehog from 3.82.5 to 3.82.6 (#5222)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 08:42:07 +02:00
Mario Rodriguez Lopez 9d4fa55c13 feat(ecs): Ensure ECS task definitions host's process namespace is not shared (#5146)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-26 18:24:21 -04:00
Mario Rodriguez Lopez ff05ce4da1 feat(ecs): Ensure ECS containers have read-only access to root filesystems (#5168)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-26 14:37:24 -04:00
Mario Rodriguez Lopez 0474c7995c feat(ecs): Ensure ECS containers run as non-privileged (#5214) 2024-09-26 14:05:11 -04:00
Mario Rodriguez Lopez 1a679f371f feat(ecr): Ensure ECR repositories have tag immutability configured (#5144)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-26 13:51:57 -04:00
Rubén De la Torre Vico 05f7170add feat(dms): add tags to DMS checks (#5209) 2024-09-26 13:33:28 -04:00
Rubén De la Torre Vico 19acb873af feat(glue): add tags to Glue checks (#5213) 2024-09-26 13:11:44 -04:00
Daniel Barranquero 0b566f9666 feat(dynamodb): add new check dynamodb_table_deletion_protection_enabled (#5148) 2024-09-26 11:19:57 -04:00
Rubén De la Torre Vico 67bf89537a chore(ec2): add tags to report of EC2 launch templates (#5210)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-26 10:50:02 -04:00
Daniel Barranquero d0681a9e20 fix(aws): change protected_by_backup_plan checks (#5204)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-26 10:33:12 -04:00
Rubén De la Torre Vico 31bff99b3d feat(codebuild): add tags support to projects (#5207) 2024-09-26 10:14:02 -04:00
Rubén De la Torre Vico 48c7e65a39 chore(autoscaling): deprecate check autoscaling_find_secrets_ec2_launch_configuration (#5205) 2024-09-26 10:11:54 -04:00
dependabot[bot] 1b407639f0 chore(deps): bump azure-mgmt-network from 26.0.0 to 27.0.0 (#5201)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-26 15:36:42 +02:00
Prowler Bot 4d7d5718d5 chore(regions_update): Changes in regions for AWS services (#5208)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-26 08:20:13 -04:00
dependabot[bot] 7955048e79 chore(deps-dev): bump mkdocs-material from 9.5.36 to 9.5.38 (#5206)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-26 10:51:29 +02:00
dependabot[bot] 8e0b715f12 chore(deps): bump trufflesecurity/trufflehog from 3.82.3 to 3.82.5 (#5202)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-26 10:07:17 +02:00
dependabot[bot] 1d81261d97 chore(deps): bump botocore from 1.35.26 to 1.35.27 (#5199)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-26 10:06:40 +02:00
Mario Rodriguez Lopez 114a3088a4 feat(ecs): Ensure public IP addresses are not assigned automatically (#5128)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-25 16:24:39 -04:00
Rubén De la Torre Vico bc8f3eba4d feat(backup): add tags to backup vaults and backup plans (#5194) 2024-09-25 11:02:53 -04:00
Hugo Pereira Brito 8e087196c9 feat(s3): Add new check s3_bucket_cross_account_access (#5082)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-25 10:01:52 -04:00
Amogh Bantwal 744e7ff5ac feat(threat-detection): Use IAM Identity for Cloudtrail Threat Detection instead of IP (#5166) 2024-09-25 09:15:47 -04:00
Prowler Bot 90b84b57d3 chore(regions_update): Changes in regions for AWS services (#5190)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-25 09:07:42 -04:00
Sergio Garcia 0a2b7cf152 chore(aws): improve IAM Resource Policy public logic (#5067)
Co-authored-by: Rubén De la Torre Vico <rubendltv22@gmail.com>
2024-09-25 08:33:41 -04:00
Pedro Martín ebbccd04f1 refactor(execute_check): refactor execute method (#4975) 2024-09-25 14:19:42 +02:00
dependabot[bot] 2b431fc79f chore(deps-dev): bump pylint from 3.3.0 to 3.3.1 (#5187)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 12:19:02 +02:00
dependabot[bot] fe7c3e7548 chore(deps): bump google-api-python-client from 2.146.0 to 2.147.0 (#5185)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 10:34:04 +02:00
dependabot[bot] 0e5f929044 chore(deps): bump boto3 from 1.35.24 to 1.35.26 (#5189)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 09:39:06 +02:00
Pedro Martín 47a6e28d71 refactor(output_options): remove output options from provider (#5149) 2024-09-25 09:38:21 +02:00
Jude Bae(Bae cheongho) de5742433b feat(compliance): add KISA ISMS-P compliance framework (#5086)
Co-authored-by: MZC01-JUDE <mzc01-jude@MZC01-JUDE-2.local>
2024-09-25 09:06:05 +02:00
dependabot[bot] 3fcccd0bcd chore(deps): bump botocore from 1.35.25 to 1.35.26 (#5184)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 08:42:35 +02:00
dependabot[bot] 00938cadb1 chore(deps): bump trufflesecurity/trufflehog from 3.82.2 to 3.82.3 (#5183)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 08:42:06 +02:00
Daniel Barranquero 9fb26643ba feat(dynamodb): add new check dynamodb_accelerator_cluster_in_transit_encryption_enabled (#5173)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-24 16:32:37 -04:00
Daniel Barranquero e4890f9d9d feat(dynamodb): add new check dynamodb_table_protected_by_backup_plan (#5175)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-24 12:45:12 -04:00
Hugo Pereira Brito 980b9b4770 feat(networkfirewall): change network_firewalls from list to dict (#5169) 2024-09-24 12:43:19 -04:00
Sergio Garcia 348cea67c0 fix(aws): always use audited partition (#5174) 2024-09-24 11:38:11 -04:00
Sergio Garcia f4d89066d9 feat(aws): add new check organizations_opt_out_ai_services_policy (#5152) 2024-09-24 11:37:03 -04:00
dependabot[bot] b26dc899be chore(deps-dev): bump moto from 5.0.14 to 5.0.15 (#5158)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-24 09:04:52 -04:00
Sergio Garcia 25327d618d chore(aws): handle NotAction cases in IAM policies (#5035) 2024-09-24 08:36:11 -04:00
Sergio Garcia 3951295c0c chore(organizations): improve AWS Organizations service (#5151) 2024-09-24 08:28:21 -04:00
Prowler Bot ff9c3b52d6 chore(regions_update): Changes in regions for AWS services (#5167)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-24 08:17:19 -04:00
dependabot[bot] af8c18eb4e chore(deps-dev): bump bandit from 1.7.9 to 1.7.10 (#5157)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-24 08:16:27 -04:00
dependabot[bot] 6fbfcc7f5f chore(deps): bump botocore from 1.35.24 to 1.35.25 (#5155)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-24 12:33:29 +02:00
dependabot[bot] 7c7132f9c4 chore(deps-dev): bump mkdocs-material from 9.5.35 to 9.5.36 (#5156)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-24 11:04:06 +02:00
dependabot[bot] 62e30f929c chore(deps): bump boto3 from 1.35.23 to 1.35.24 (#5154)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-24 10:06:27 +02:00
Pepe Fagoaga ddaafd5876 chore(bot): Use bot Token (#5163) 2024-09-24 10:06:00 +02:00
Mario Rodriguez Lopez 1f43e6eff9 feat(inspector2): Add more tests to inspector2_is_enabled check (#5150) 2024-09-23 15:06:34 -04:00
Daniel Barranquero aa118c05c5 feat(rds): add new check rds_cluster_non_default_port (#5113) 2024-09-23 15:05:56 -04:00
Hugo Pereira Brito cca17b9378 feat(cloudfront): add new check cloudfront_distributions_s3_origin_non_existing_bucket (#4996)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-23 12:43:03 -04:00
Pedro Martín 14ed19e3a8 fix(iam): fill resource id with inline policy entity (#5120) 2024-09-23 10:54:38 -04:00
dependabot[bot] 8caf8f794c chore(deps): bump azure-mgmt-cosmosdb from 9.5.1 to 9.6.0 (#5111)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 09:38:15 -04:00
dependabot[bot] cba9ad61e4 chore(deps): bump msgraph-sdk from 1.7.0 to 1.8.0 (#5110)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 08:48:42 -04:00
dependabot[bot] e64a0eff0f chore(deps): bump botocore from 1.35.23 to 1.35.24 (#5140)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 08:01:40 -04:00
dependabot[bot] 23c65b8fde chore(deps): bump pandas from 2.2.2 to 2.2.3 (#5139)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 10:33:38 +02:00
dependabot[bot] a7c93f3237 chore(deps-dev): bump pylint from 3.2.7 to 3.3.0 (#5138)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 09:41:04 +02:00
dependabot[bot] 7b9402f3d0 chore(deps): bump kubernetes from 30.1.0 to 31.0.0 (#5137)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 08:29:49 +02:00
Sergio Garcia 4badcca4f8 fix(gcp): add default project for org level checks (#5003) 2024-09-20 20:39:35 +02:00
Hugo Pereira Brito c6daa60f26 feat(elasticache): add check elasticache_redis_cluster_auth_enabled (#4830)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-20 12:18:08 -04:00
Harshit Raj Singh f9aa2bb8be fix(lightsail): Remove second call to is_resource_filtered (#5044) 2024-09-20 11:39:03 -04:00
Rubén De la Torre Vico 66ac395705 chore(README): update checks summary table (#5119) 2024-09-20 11:27:19 -04:00
Sergio Garcia 16a251254e fix(gcp): solve errors in GCP services (#5016) 2024-09-20 11:06:57 -04:00
Sergio Garcia 751958907c fix(vpc): check all routes tables in subnet (#5081) 2024-09-20 10:13:16 -04:00
Sergio Garcia 60012ab19d chore(deps): update docs dependencies (#5098) 2024-09-20 10:13:09 -04:00
dependabot[bot] 65d7ba020b chore(deps): bump boto3 from 1.35.21 to 1.35.23 (#5115)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 09:13:09 -04:00
Sergio Garcia 9456c6198a chore(ssm): add trusted accounts variable to ssm check (#5005)
Co-authored-by: pedrooot <pedromarting3@gmail.com>
2024-09-20 09:12:48 -04:00
Sergio Garcia 45ce1a0650 fix(asff): include status extended in ASFF output (#5097) 2024-09-20 09:08:13 -04:00
dependabot[bot] 4c5db5295c chore(deps): bump botocore from 1.35.22 to 1.35.23 (#5109)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 14:18:55 +02:00
dependabot[bot] a2ad0cdf30 chore(deps): bump azure-identity from 1.17.1 to 1.18.0 (#5108)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 13:29:43 +02:00
dependabot[bot] 0c70a64e84 chore(deps): bump slack-sdk from 3.33.0 to 3.33.1 (#5107)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 11:57:41 +02:00
Mario Rodriguez Lopez 73c96f8346 feat(sagemaker): Ensure SageMaker Endpoint Production Variants have Initial Instance Count greater than one (#5045)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-19 15:16:56 -04:00
Amogh Bantwal 0974c5f333 feat(slack): add more information about critical findings (#5042) 2024-09-19 14:02:09 -04:00
Hugo Pereira Brito 7db0746416 feat(guardduty): add new check guardduty_rds_protection_enabled (#5100) 2024-09-19 13:52:17 -04:00
dependabot[bot] 8f0bf5e896 chore(deps-dev): bump pytest-env from 1.1.4 to 1.1.5 (#5090)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-19 12:29:43 -04:00
Pedro Martín 57abe1c839 fix(accessanalyzer): refactor accessanalyzer enabled fixer test (#5026) 2024-09-19 11:09:03 -04:00
Daniel Barranquero 43183962ad feat(aws): Add new check to ensure RDS instances are not using default database engine ports (#4973)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-19 10:14:46 -04:00
Daniel Barranquero 87948b458e feat(guardduty): add new check guardduty_s3_protection_enabled (#5087)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-19 10:10:39 -04:00
dependabot[bot] ab5c3eb4f8 chore(deps): bump botocore from 1.35.21 to 1.35.22 (#5089)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 09:30:24 -04:00
Rubén De la Torre Vico 320a2a2c77 feat(awslambda): add new check awslambda_function_vpc_multi_az (#4816)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-19 09:30:04 -04:00
Sergio Garcia dbc8e140e3 chore(docs): change ResourceType link of Security Hub (#5063) 2024-09-19 07:25:41 -04:00
Hugo Pereira Brito 21ac395d4c fix(elasticache): get correct automatic failover attribute (#5084)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-18 18:29:43 -04:00
Mario Rodriguez Lopez 8a8c2b5097 feat(ecs): add new check ecs_task_definitions_host_networking_mode_users (#5088)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-18 18:28:31 -04:00
dependabot[bot] 3bea772c6b chore(deps): bump slack-sdk from 3.32.0 to 3.33.0 (#5069)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 18:28:20 -04:00
Lefteris 34679c98d6 feat(dms): new check dms_endpoint_ssl_enabled (#4968)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
Co-authored-by: Rubén De la Torre Vico <rubendltv22@gmail.com>
2024-09-18 17:46:56 -04:00
dependabot[bot] 2b41445d57 chore(deps): bump boto3 from 1.35.19 to 1.35.21 (#5085)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 17:21:02 -04:00
dependabot[bot] 796c87bc93 chore(deps): bump google-api-python-client from 2.145.0 to 2.146.0 (#5070)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 16:32:09 -04:00
dependabot[bot] a83e08aa9e chore(deps-dev): bump vulture from 2.11 to 2.12 (#5071)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 13:59:25 -04:00
Hugo Pereira Brito ae794c7c32 feat(cloudfront): Ensure Cloudfront distributions have origin failover configured (#4868)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-18 13:26:35 -04:00
dependabot[bot] edc78bfd6b chore(deps): bump botocore from 1.35.20 to 1.35.21 (#5073)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 13:18:17 -04:00
dependabot[bot] 9263adeb78 chore(deps): bump azure-storage-blob from 12.22.0 to 12.23.0 (#5072)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 12:19:46 -04:00
Prowler Bot bfdc87723b chore(regions_update): Changes in regions for AWS services (#5080)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-18 11:33:01 -04:00
Rubén De la Torre Vico 8d23e81b1c feat(elb): add new check elb_connection_draining_enabled (#5014)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-18 10:49:33 -04:00
Daniel Barranquero f0cd924016 feat(neptune): add new check neptune_cluster_copy_tags_to_snapshots (#5062)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-18 09:23:44 -04:00
Mario Rodriguez Lopez c425e8249b fix(inspector2): Ensure Inspector2 is enabled for ECR, EC2, Lambda and Lambda Code (#5061)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-17 14:01:19 -04:00
Daniel Barranquero 1ece8bbcd6 feat(neptune): add new check neptune_cluster_snapshot_encrypted (#5058)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-17 13:16:43 -04:00
Daniel Barranquero 5fb2d7c3ce feat(neptune): add new check neptune_cluster_integration_cloudwatch_logs (#5048)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-17 12:20:25 -04:00
Prowler Bot 64aebe84fe chore(regions_update): Changes in regions for AWS services (#5059)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-17 11:52:41 -04:00
Rubén De la Torre Vico de831b0abe chore(AWS): match all AWS resource types with SecurityHub supported types in metadata (#4882)
Co-authored-by: Sergio <sergio@prowler.com>
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-17 11:40:45 -04:00
dependabot[bot] 68af4f6c73 chore(deps): bump botocore from 1.35.19 to 1.35.20 (#5053)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-17 11:39:24 -04:00
dependabot[bot] 52981b54b9 chore(deps): bump trufflesecurity/trufflehog from 3.82.1 to 3.82.2 (#5052)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-17 08:36:42 -04:00
dependabot[bot] a366594714 chore(deps): bump boto3 from 1.35.16 to 1.35.19 (#5049)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 15:16:44 -04:00
Hugo Pereira Brito 1fb36f316b fix(cloudfront): duplicated link in cloudfront_distributions_https_sni_enabled check (#5047) 2024-09-16 15:16:26 -04:00
dependabot[bot] 30ffa8f00b chore(deps): bump azure-mgmt-containerservice from 31.0.0 to 32.0.0 (#5036)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 13:45:01 -04:00
Prowler Bot 5855918ade chore(regions_update): Changes in regions for AWS services (#5041)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-16 13:44:47 -04:00
dependabot[bot] f9005c875f chore(deps): bump botocore from 1.35.18 to 1.35.19 (#5037)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 12:52:59 -04:00
Mario Rodriguez Lopez 91bf99ca45 feat(ec2): Ensure EC2 launch templates do not assign public IPs (#4852)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-16 12:52:40 -04:00
dependabot[bot] 8176063fef chore(deps): bump dash from 2.18.0 to 2.18.1 (#5024)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 11:25:08 -04:00
Mario Rodriguez Lopez 3373822240 feat(ec2): EBS Volumes Should Be Covered by a Backup Plan (#5028)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-16 11:23:23 -04:00
Hugo Pereira Brito 7e16702b2f feat(cloudfront): add cloudfront_distributions_origin_traffic_encrypted check to ensure traffic encryption to custom origins (#4958)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-16 09:12:37 -04:00
Daniel Barranquero f54b64f1f8 feat(rds): add new check rds_instance_inside_vpc (#5029)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-16 08:56:39 -04:00
dependabot[bot] 2c337ab3f6 chore(deps-dev): bump mkdocs-git-revision-date-localized-plugin from 1.2.8 to 1.2.9 (#5023)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-13 14:44:33 -04:00
dependabot[bot] 5279d937d7 chore(deps): bump botocore from 1.35.17 to 1.35.18 (#5021)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-13 11:24:55 -04:00
Hugo Pereira Brito 48c31a1616 feat(cloudfront): Add new cloudfront_distributions_s3_origin_access_control check to ensure OAC is configured in distributions (#4939)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-13 10:51:49 -04:00
Rubén De la Torre Vico 917a2ad0fe docs(check): change where extract ResourceTypes (#5030) 2024-09-13 10:51:09 -04:00
Rubén De la Torre Vico 8cfc4c56cf docs(dev-guide): refer poetry docs for installation (#5031) 2024-09-13 10:45:57 -04:00
Prowler Bot 99e9e42a17 chore(regions_update): Changes in regions for AWS services (#5027)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-13 10:38:08 -04:00
dependabot[bot] 13c95ba131 chore(deps): bump trufflesecurity/trufflehog from 3.81.10 to 3.82.1 (#5025)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-13 08:59:06 -04:00
LefterisXefteris 600a8c7804 chore(aws): add mixed regions test for s3_access_point_public_access_block (#4877)
Co-authored-by: Lefteris Gilmaz <lefterisgilmaz@Lefteriss-MacBook-Pro.local>
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-12 15:58:39 -04:00
Hugo Pereira Brito 64fb52fc5e feat(cloudfront): add new check cloudfront_distributions_custom_ssl_certificate (#4959)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-12 15:00:48 -04:00
Mario Rodriguez Lopez 92b6e7230d feat(ec2): Amazon EC2 Instances Should Not Use Multiple ENIs (#4935)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-12 14:29:36 -04:00
Hugo Pereira Brito cc8bc781c1 feat(elasticache): Ensure Redis replication groups have automatic failover enabled (#4853)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-12 12:23:15 -04:00
Hugo Pereira Brito edbe463d73 feat(cloudfront): Add new check cloudfront_distributions_default_root_object (#4938)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-12 10:58:24 -04:00
Sergio Garcia 8ace8c01cf chore(refactor): make Provider generation global (#4961)
Co-authored-by: pedrooot <pedromarting3@gmail.com>
2024-09-12 16:56:58 +02:00
Hugo Pereira Brito 8f37252676 feat(cloudfront): Ensure distributions use SNI to serve HTTPS requests (#4888)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-12 09:28:26 -04:00
Mario Rodriguez Lopez c0c59968bf feat(ec2): Ensure both VPN tunnels for an AWS Site-to-Site VPN connection are UP (#4948) 2024-09-12 08:26:35 -04:00
dependabot[bot] 9f5a909be3 chore(deps): bump msgraph-sdk from 1.6.0 to 1.7.0 (#5013)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-12 14:10:50 +02:00
dependabot[bot] 90975bdadc chore(deps): bump pytz from 2024.1 to 2024.2 (#5012)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-12 11:32:58 +02:00
dependabot[bot] 7d1fad9eb7 chore(deps): bump botocore from 1.35.16 to 1.35.17 (#5011)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-12 10:50:07 +02:00
dependabot[bot] 983c79ad3b chore(deps): bump boto3 from 1.35.15 to 1.35.16 (#5010)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-12 09:16:53 +02:00
Mario Rodriguez Lopez 96e73fcb63 feat(ec2): Amazon EC2 Paravirtual Instance Types Should Not Be Used (#4922)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-11 15:56:20 -04:00
Pedro Martín 70a3736073 fix(awslamba): add audit config to lambda_client in tests (#4999) 2024-09-11 12:15:22 -04:00
Pedro Martín 1e8e8ba65c fix(iam-gcp): add getters in iam_service for gcp (#4998) 2024-09-11 11:01:58 -04:00
dependabot[bot] 359a1f2c8e chore(deps): bump botocore from 1.35.15 to 1.35.16 (#4989)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-11 10:53:18 -04:00
Mario Rodriguez Lopez 2e4f8cbfc7 feat(ec2): Ensure not default Network Access Control Lists are used (#4917)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-09-11 09:55:18 -04:00
Prowler Bot 482aee0d9d chore(regions_update): Changes in regions for AWS services (#4995)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-09-11 09:52:28 -04:00
Daniel Barranquero 0ae3374e81 feat(aws): Add new check to ensure Aurora MySQL DB Clusters publish audit logs to CloudWatch logs (#4916)
Co-authored-by: Sergio <sergio@prowler.com>
2024-09-11 09:10:49 -04:00
Mario Rodriguez Lopez ddc088859e feat(vpc): Ensure Amazon EC2 Is Configured to Use VPC Endpoints Created for the Amazon EC2 Service (#4872) 2024-09-11 09:08:25 -04:00
dependabot[bot] 5e3da2d687 chore(deps): bump google-api-python-client from 2.144.0 to 2.145.0 (#4990)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-11 13:28:13 +02:00
226 changed files with 7807 additions and 1347 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
with:
fetch-depth: 0
- name: TruffleHog OSS
uses: trufflesecurity/trufflehog@v3.82.6
uses: trufflesecurity/trufflehog@v3.82.7
with:
path: ./
base: ${{ github.event.repository.default_branch }}
@@ -12,7 +12,11 @@ Originally based on [org-multi-account](https://github.com/prowler-cloud/prowler
## Architecture Explanation
The solution is designed to be very simple. Prowler is run via an ECS Task definition that launches a single Fargate container. This Task Definition is executed on a schedule using an EventBridge Rule.
The solution is designed to be very simple. Prowler is run via an ECS Task definition that launches a single Fargate container. This Task Definition is executed on a schedule using an EventBridge Rule.
## Prerequisites
This solution assumes that you have a VPC architecture with two redundant subnets that can reach the AWS API endpoints (e.g. PrivateLink, NAT Gateway, etc.).
## CloudFormation Templates
@@ -59,9 +63,9 @@ The logs that are generated and sent to Cloudwatch are error logs, and assessmen
## Instructions
1. Create a Private ECR Repository in the account that will host the Prowler container. The Audit account is recommended, but any account can be used.
2. Configure the .awsvariables file. Note the ROLE name chosen as it will be the CrossAccountRole.
3. Follow the steps from "View Push Commands" to build and upload the container image. You need to have Docker and AWS CLI installed, and use the cli to login to the account first. After upload note the Image URI, as it is required for the CF-Prowler-ECS template.
4. Make sure SecurityHub is enabled in every account in AWS Organizations, and that the SecurityHub integration is enabled as explained in [Prowler - Security Hub Integration](https://github.com/prowler-cloud/prowler#security-hub-integration)
2. Configure the .awsvariables file. Note the ROLE name chosen as it will be the CrossAccountRole.
3. Follow the steps from "View Push Commands" to build and upload the container image. Substitute step 2 with the build command provided in the Dockerfile. You need to have Docker and AWS CLI installed, and use the cli to login to the account first. After upload note the Image URI, as it is required for the CF-Prowler-ECS template. Ensure that you pay attention to the architecture while performing the docker build command. A common mistake is not specifying the architecture and then building on Apple silicon. Your task will fail with *exec /home/prowler/.local/bin/prowler: exec format error*.
4. Make sure SecurityHub is enabled in every account in AWS Organizations, and that the SecurityHub integration is enabled as explained in [Prowler - Security Hub Integration](https://github.com/prowler-cloud/prowler#security-hub-integration)
5. Deploy **CF-Prowler-CrossAccountRole.yml** in the Master Account as a single stack. You will have to choose the CrossAccountRole name (ProwlerXA-Role by default) and the ProwlerTaskRoleName (ProwlerECSTask-Role by default)
6. Deploy **CF-Prowler-CrossAccountRole.yml** in every Member Account as a StackSet. Choose the same CrossAccountName and ProwlerTaskRoleName as the previous step.
7. Deploy **CF-Prowler-IAM.yml** in the account that will host the Prowler container (the same from step 1). The following template parameters must be provided:
@@ -91,4 +95,4 @@ If you permission find errors in the CloudWatch logs, the culprit might be a [Se
## Upgrading Prowler
Prowler version is controlled by the PROWLERVER argument in the Dockerfile, change it to the desired version and follow the ECR Push Commands to update the container image.
Old images can be deleted from the ECR Repository after the new image is confirmed to work. They will show as "untagged" as only one image can hold the "latest" tag.
Old images can be deleted from the ECR Repository after the new image is confirmed to work. They will show as "untagged" as only one image can hold the "latest" tag.
@@ -68,7 +68,7 @@ for accountId in ${ACCOUNTS_IN_ORGS}; do
# Run Prowler
echo -e "Assessing AWS Account: ${accountId}, using Role: ${ROLE} on $(date)"
# Pipe stdout to /dev/null to reduce unnecessary Cloudwatch logs
prowler aws -R arn:"${PARTITION}":iam::"${accountId}":role/"${ROLE}" -q -S -f "${REGION}" > /dev/null
prowler aws -R arn:"${PARTITION}":iam::"${accountId}":role/"${ROLE}" --security-hub --send-sh-only-fails -f "${REGION}" > /dev/null
TOTAL_SEC=$((SECONDS - START_TIME))
printf "Completed AWS Account: ${accountId} in %02dh:%02dm:%02ds" $((TOTAL_SEC / 3600)) $((TOTAL_SEC % 3600 / 60)) $((TOTAL_SEC % 60))
echo ""
@@ -60,24 +60,42 @@ Resources:
Effect: Allow
Resource: "*"
Action:
- ds:ListAuthorizedApplications
- account:Get*
- appstream:Describe*
- appstream:List*
- backup:List*
- cloudtrail:GetInsightSelectors
- codeartifact:List*
- codebuild:BatchGet*
- cognito-idp:GetUserPoolMfaConfig
- dlm:Get*
- drs:Describe*
- ds:Describe*
- ds:Get*
- ds:List*
- dynamodb:GetResourcePolicy
- ec2:GetEbsEncryptionByDefault
- ec2:GetSnapshotBlockPublicAccessState
- ec2:GetInstanceMetadataDefaults
- ecr:Describe*
- ecr:GetRegistryScanningConfiguration
- elasticfilesystem:DescribeBackupPolicy
- glue:GetConnections
- glue:GetSecurityConfiguration
- glue:GetSecurityConfiguration*
- glue:SearchTables
- lambda:GetFunction
- lambda:GetFunction*
- logs:FilterLogEvents
- lightsail:GetRelationalDatabases
- macie2:GetMacieSession
- s3:GetAccountPublicAccessBlock
- shield:DescribeProtection
- shield:GetSubscriptionState
- ssm:GetDocument
- ssm-incidents:List*
- support:Describe*
- tag:GetTagKeys
- PolicyName: Prowler-Security-Hub
PolicyDocument:
Version: 2012-10-17
Statement:
- wellarchitected:List*
- Sid: AllowProwlerSecurityHub
Effect: Allow
Resource: "*"
@@ -62,7 +62,7 @@ Resources:
awslogs-stream-prefix: ecs
Cpu: 1024
ExecutionRoleArn: !Ref ECSExecutionRole
Memory: 2048
Memory: 8192
NetworkMode: awsvpc
TaskRoleArn: !Ref ProwlerTaskRole
Family: SecurityHubProwlerTask
@@ -97,9 +97,15 @@ Outputs:
ECSExecutionRoleARN:
Description: ARN of the ECS Task Execution Role
Value: !GetAtt ECSExecutionRole.Arn
Export:
Name: ECSExecutionRoleArn
ProwlerTaskRoleARN:
Description: ARN of the ECS Prowler Task Role
Value: !GetAtt ProwlerTaskRole.Arn
Export:
Name: ProwlerTaskRoleArn
ECSEventRoleARN:
Description: ARN of the Eventbridge Task Role
Value: !GetAtt ECSEventRole.Arn
Export:
Name: ECSEventRoleARN
@@ -7,6 +7,7 @@ At the time of writing this documentation the available Azure Clouds from differ
- AzureCloud
- AzureChinaCloud
- AzureUSGovernment
- AzureGermanCloud
If you want to change the default one you must include the flag `--azure-region`, i.e.:
+39
View File
@@ -0,0 +1,39 @@
# Scan Inventory
The scan-inventory feature is a tool that generates a JSON report within the `/output/inventory/<provider>` directory and the scanned service. This feature allows you to perform a inventory of the resources existing in your provider that are scanned by Prowler.
## Usage
To use the scan-inventory feature, run Prowler with the `--scan-inventory` option. For example:
```
prowler <provider> --scan-inventory
```
This will generate a JSON report within the `/output/inventory/<provider>` directory and the scanned service.
## Output Directory Contents
The contents of the `/output/<provider>` directory and the scanned service depend on the Prowler execution. This directory contains all the information gathered during scanning, including a JSON report containing all the gathered information.
## Limitations
The scan-inventory feature has some limitations. For example:
* It is only available for the AWS provider.
* It only contains the information retrieved by Prowler during the execution.
## Example
Here's an example of how to use the scan-inventory feature and the contents of the `/output/inventory/<provider>` directory and the scanned service:
`prowler aws -s ec2 --scan-inventory`
```
/output/inventory/aws directory
|
|-- ec2
| |
| |-- ec2_output.json
```
In this example, Prowler is run with the `-s ec2` and `--scan-inventory` options for the AWS provider. The `/output/inventory/aws` directory contains a JSON report showing all the information gathered during scanning.
+1
View File
@@ -55,6 +55,7 @@ nav:
- Dashboard: tutorials/dashboard.md
- Fixer (remediations): tutorials/fixer.md
- Quick Inventory: tutorials/quick-inventory.md
- Scan Inventory: tutorials/scan-inventory.md
- Slack Integration: tutorials/integrations.md
- Configuration File: tutorials/configuration_file.md
- Logging: tutorials/logging.md
Generated
+65 -57
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "about-time"
@@ -758,17 +758,17 @@ files = [
[[package]]
name = "boto3"
version = "1.35.28"
version = "1.35.29"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "boto3-1.35.28-py3-none-any.whl", hash = "sha256:dc088b86a14f17d3cd2e96915c6ccfd31bce640dfe9180df579ed311bc6bf0fc"},
{file = "boto3-1.35.28.tar.gz", hash = "sha256:8960fc458b9ba3c8a9890a607c31cee375db821f39aefaec9ff638248e81644a"},
{file = "boto3-1.35.29-py3-none-any.whl", hash = "sha256:2244044cdfa8ac345d7400536dc15a4824835e7ec5c55bc267e118af66bb27db"},
{file = "boto3-1.35.29.tar.gz", hash = "sha256:7bbb1ee649e09e956952285782cfdebd7e81fc78384f48dfab3d66c6eaf3f63f"},
]
[package.dependencies]
botocore = ">=1.35.28,<1.36.0"
botocore = ">=1.35.29,<1.36.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0"
@@ -1414,13 +1414,13 @@ files = [
[[package]]
name = "email-validator"
version = "2.1.1"
version = "2.2.0"
description = "A robust email address syntax and deliverability validation library."
optional = false
python-versions = ">=3.8"
files = [
{file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"},
{file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"},
{file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"},
{file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"},
]
[package.dependencies]
@@ -2542,13 +2542,13 @@ dev = ["click", "codecov", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkd
[[package]]
name = "mkdocs-material"
version = "9.5.38"
version = "9.5.39"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
{file = "mkdocs_material-9.5.38-py3-none-any.whl", hash = "sha256:d4779051d52ba9f1e7e344b34de95449c7c366c212b388e4a2db9a3db043c228"},
{file = "mkdocs_material-9.5.38.tar.gz", hash = "sha256:1843c5171ad6b489550aeaf7358e5b7128cc03ddcf0fb4d91d19aa1e691a63b8"},
{file = "mkdocs_material-9.5.39-py3-none-any.whl", hash = "sha256:0f2f68c8db89523cb4a59705cd01b4acd62b2f71218ccb67e1e004e560410d2b"},
{file = "mkdocs_material-9.5.39.tar.gz", hash = "sha256:25faa06142afa38549d2b781d475a86fb61de93189f532b88e69bf11e5e5c3be"},
]
[package.dependencies]
@@ -2598,13 +2598,13 @@ test = ["pytest", "pytest-cov"]
[[package]]
name = "moto"
version = "5.0.15"
version = "5.0.16"
description = ""
optional = false
python-versions = ">=3.8"
files = [
{file = "moto-5.0.15-py2.py3-none-any.whl", hash = "sha256:fa1e92ffb55dbfb9fa92a2115a88c32481b75aa3fbd24075d1f29af2f9becffa"},
{file = "moto-5.0.15.tar.gz", hash = "sha256:57aa8c2af417cc64a0ddfe63e5bcd1ada90f5079b73cdd1f74c4e9fb30a1a7e6"},
{file = "moto-5.0.16-py2.py3-none-any.whl", hash = "sha256:4ce1f34830307f7b3d553d77a7ef26066ab3b70006203d4226b048c9d11a3be4"},
{file = "moto-5.0.16.tar.gz", hash = "sha256:f4afb176a964cd7a70da9bc5e053d43109614ce3cab26044bcbb53610435dff4"},
]
[package.dependencies]
@@ -3467,18 +3467,19 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
[[package]]
name = "py-ocsf-models"
version = "0.1.1"
version = "0.2.0"
description = "This is a Python implementation of the OCSF models. The models are used to represent the data of the OCSF Schema defined in https://schema.ocsf.io/."
optional = false
python-versions = "<3.13,>=3.9"
files = [
{file = "py_ocsf_models-0.1.1-py3-none-any.whl", hash = "sha256:c6ea465fda85470b938a48da65b1f19664f6d83820ebe849ef5551094e6768de"},
{file = "py_ocsf_models-0.1.1.tar.gz", hash = "sha256:b0f2d4495a2596793f75e61a1ba218edea3a2d17a2b9911d46ee0fa623cc657b"},
{file = "py_ocsf_models-0.2.0-py3-none-any.whl", hash = "sha256:ac75fd21077694b343ebaad3479194db113c274879b114277560ff287d5cd7b5"},
{file = "py_ocsf_models-0.2.0.tar.gz", hash = "sha256:3e12648d05329e6776a0e6b1ffea87a3eb60aa7d8cb2c4afd69e5724f443ce03"},
]
[package.dependencies]
email-validator = "2.1.1"
pydantic = "1.10.15"
cryptography = "43.0.1"
email-validator = "2.2.0"
pydantic = "1.10.18"
[[package]]
name = "py-partiql-parser"
@@ -3543,47 +3544,54 @@ files = [
[[package]]
name = "pydantic"
version = "1.10.15"
version = "1.10.18"
description = "Data validation and settings management using python type hints"
optional = false
python-versions = ">=3.7"
files = [
{file = "pydantic-1.10.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22ed12ee588b1df028a2aa5d66f07bf8f8b4c8579c2e96d5a9c1f96b77f3bb55"},
{file = "pydantic-1.10.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75279d3cac98186b6ebc2597b06bcbc7244744f6b0b44a23e4ef01e5683cc0d2"},
{file = "pydantic-1.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50f1666a9940d3d68683c9d96e39640f709d7a72ff8702987dab1761036206bb"},
{file = "pydantic-1.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82790d4753ee5d00739d6cb5cf56bceb186d9d6ce134aca3ba7befb1eedbc2c8"},
{file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d207d5b87f6cbefbdb1198154292faee8017d7495a54ae58db06762004500d00"},
{file = "pydantic-1.10.15-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e49db944fad339b2ccb80128ffd3f8af076f9f287197a480bf1e4ca053a866f0"},
{file = "pydantic-1.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:d3b5c4cbd0c9cb61bbbb19ce335e1f8ab87a811f6d589ed52b0254cf585d709c"},
{file = "pydantic-1.10.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3d5731a120752248844676bf92f25a12f6e45425e63ce22e0849297a093b5b0"},
{file = "pydantic-1.10.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c365ad9c394f9eeffcb30a82f4246c0006417f03a7c0f8315d6211f25f7cb654"},
{file = "pydantic-1.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3287e1614393119c67bd4404f46e33ae3be3ed4cd10360b48d0a4459f420c6a3"},
{file = "pydantic-1.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be51dd2c8596b25fe43c0a4a59c2bee4f18d88efb8031188f9e7ddc6b469cf44"},
{file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6a51a1dd4aa7b3f1317f65493a182d3cff708385327c1c82c81e4a9d6d65b2e4"},
{file = "pydantic-1.10.15-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4e316e54b5775d1eb59187f9290aeb38acf620e10f7fd2f776d97bb788199e53"},
{file = "pydantic-1.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:0d142fa1b8f2f0ae11ddd5e3e317dcac060b951d605fda26ca9b234b92214986"},
{file = "pydantic-1.10.15-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ea210336b891f5ea334f8fc9f8f862b87acd5d4a0cbc9e3e208e7aa1775dabf"},
{file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3453685ccd7140715e05f2193d64030101eaad26076fad4e246c1cc97e1bb30d"},
{file = "pydantic-1.10.15-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bea1f03b8d4e8e86702c918ccfd5d947ac268f0f0cc6ed71782e4b09353b26f"},
{file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:005655cabc29081de8243126e036f2065bd7ea5b9dff95fde6d2c642d39755de"},
{file = "pydantic-1.10.15-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:af9850d98fc21e5bc24ea9e35dd80a29faf6462c608728a110c0a30b595e58b7"},
{file = "pydantic-1.10.15-cp37-cp37m-win_amd64.whl", hash = "sha256:d31ee5b14a82c9afe2bd26aaa405293d4237d0591527d9129ce36e58f19f95c1"},
{file = "pydantic-1.10.15-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5e09c19df304b8123938dc3c53d3d3be6ec74b9d7d0d80f4f4b5432ae16c2022"},
{file = "pydantic-1.10.15-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7ac9237cd62947db00a0d16acf2f3e00d1ae9d3bd602b9c415f93e7a9fc10528"},
{file = "pydantic-1.10.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:584f2d4c98ffec420e02305cf675857bae03c9d617fcfdc34946b1160213a948"},
{file = "pydantic-1.10.15-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbc6989fad0c030bd70a0b6f626f98a862224bc2b1e36bfc531ea2facc0a340c"},
{file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d573082c6ef99336f2cb5b667b781d2f776d4af311574fb53d908517ba523c22"},
{file = "pydantic-1.10.15-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6bd7030c9abc80134087d8b6e7aa957e43d35714daa116aced57269a445b8f7b"},
{file = "pydantic-1.10.15-cp38-cp38-win_amd64.whl", hash = "sha256:3350f527bb04138f8aff932dc828f154847fbdc7a1a44c240fbfff1b57f49a12"},
{file = "pydantic-1.10.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:51d405b42f1b86703555797270e4970a9f9bd7953f3990142e69d1037f9d9e51"},
{file = "pydantic-1.10.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a980a77c52723b0dc56640ced396b73a024d4b74f02bcb2d21dbbac1debbe9d0"},
{file = "pydantic-1.10.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f1a1fb467d3f49e1708a3f632b11c69fccb4e748a325d5a491ddc7b5d22383"},
{file = "pydantic-1.10.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:676ed48f2c5bbad835f1a8ed8a6d44c1cd5a21121116d2ac40bd1cd3619746ed"},
{file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:92229f73400b80c13afcd050687f4d7e88de9234d74b27e6728aa689abcf58cc"},
{file = "pydantic-1.10.15-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2746189100c646682eff0bce95efa7d2e203420d8e1c613dc0c6b4c1d9c1fde4"},
{file = "pydantic-1.10.15-cp39-cp39-win_amd64.whl", hash = "sha256:394f08750bd8eaad714718812e7fab615f873b3cdd0b9d84e76e51ef3b50b6b7"},
{file = "pydantic-1.10.15-py3-none-any.whl", hash = "sha256:28e552a060ba2740d0d2aabe35162652c1459a0b9069fe0db7f4ee0e18e74d58"},
{file = "pydantic-1.10.15.tar.gz", hash = "sha256:ca832e124eda231a60a041da4f013e3ff24949d94a01154b137fc2f2a43c3ffb"},
{file = "pydantic-1.10.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e405ffcc1254d76bb0e760db101ee8916b620893e6edfbfee563b3c6f7a67c02"},
{file = "pydantic-1.10.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e306e280ebebc65040034bff1a0a81fd86b2f4f05daac0131f29541cafd80b80"},
{file = "pydantic-1.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11d9d9b87b50338b1b7de4ebf34fd29fdb0d219dc07ade29effc74d3d2609c62"},
{file = "pydantic-1.10.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b661ce52c7b5e5f600c0c3c5839e71918346af2ef20062705ae76b5c16914cab"},
{file = "pydantic-1.10.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c20f682defc9ef81cd7eaa485879ab29a86a0ba58acf669a78ed868e72bb89e0"},
{file = "pydantic-1.10.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c5ae6b7c8483b1e0bf59e5f1843e4fd8fd405e11df7de217ee65b98eb5462861"},
{file = "pydantic-1.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:74fe19dda960b193b0eb82c1f4d2c8e5e26918d9cda858cbf3f41dd28549cb70"},
{file = "pydantic-1.10.18-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:72fa46abace0a7743cc697dbb830a41ee84c9db8456e8d77a46d79b537efd7ec"},
{file = "pydantic-1.10.18-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef0fe7ad7cbdb5f372463d42e6ed4ca9c443a52ce544472d8842a0576d830da5"},
{file = "pydantic-1.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a00e63104346145389b8e8f500bc6a241e729feaf0559b88b8aa513dd2065481"},
{file = "pydantic-1.10.18-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae6fa2008e1443c46b7b3a5eb03800121868d5ab6bc7cda20b5df3e133cde8b3"},
{file = "pydantic-1.10.18-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9f463abafdc92635da4b38807f5b9972276be7c8c5121989768549fceb8d2588"},
{file = "pydantic-1.10.18-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3445426da503c7e40baccefb2b2989a0c5ce6b163679dd75f55493b460f05a8f"},
{file = "pydantic-1.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:467a14ee2183bc9c902579bb2f04c3d3dac00eff52e252850509a562255b2a33"},
{file = "pydantic-1.10.18-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:efbc8a7f9cb5fe26122acba1852d8dcd1e125e723727c59dcd244da7bdaa54f2"},
{file = "pydantic-1.10.18-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24a4a159d0f7a8e26bf6463b0d3d60871d6a52eac5bb6a07a7df85c806f4c048"},
{file = "pydantic-1.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b74be007703547dc52e3c37344d130a7bfacca7df112a9e5ceeb840a9ce195c7"},
{file = "pydantic-1.10.18-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcb20d4cb355195c75000a49bb4a31d75e4295200df620f454bbc6bdf60ca890"},
{file = "pydantic-1.10.18-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:46f379b8cb8a3585e3f61bf9ae7d606c70d133943f339d38b76e041ec234953f"},
{file = "pydantic-1.10.18-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbfbca662ed3729204090c4d09ee4beeecc1a7ecba5a159a94b5a4eb24e3759a"},
{file = "pydantic-1.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:c6d0a9f9eccaf7f438671a64acf654ef0d045466e63f9f68a579e2383b63f357"},
{file = "pydantic-1.10.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d5492dbf953d7d849751917e3b2433fb26010d977aa7a0765c37425a4026ff1"},
{file = "pydantic-1.10.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe734914977eed33033b70bfc097e1baaffb589517863955430bf2e0846ac30f"},
{file = "pydantic-1.10.18-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15fdbe568beaca9aacfccd5ceadfb5f1a235087a127e8af5e48df9d8a45ae85c"},
{file = "pydantic-1.10.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c3e742f62198c9eb9201781fbebe64533a3bbf6a76a91b8d438d62b813079dbc"},
{file = "pydantic-1.10.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:19a3bd00b9dafc2cd7250d94d5b578edf7a0bd7daf102617153ff9a8fa37871c"},
{file = "pydantic-1.10.18-cp37-cp37m-win_amd64.whl", hash = "sha256:2ce3fcf75b2bae99aa31bd4968de0474ebe8c8258a0110903478bd83dfee4e3b"},
{file = "pydantic-1.10.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:335a32d72c51a313b33fa3a9b0fe283503272ef6467910338e123f90925f0f03"},
{file = "pydantic-1.10.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:34a3613c7edb8c6fa578e58e9abe3c0f5e7430e0fc34a65a415a1683b9c32d9a"},
{file = "pydantic-1.10.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9ee4e6ca1d9616797fa2e9c0bfb8815912c7d67aca96f77428e316741082a1b"},
{file = "pydantic-1.10.18-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23e8ec1ce4e57b4f441fc91e3c12adba023fedd06868445a5b5f1d48f0ab3682"},
{file = "pydantic-1.10.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:44ae8a3e35a54d2e8fa88ed65e1b08967a9ef8c320819a969bfa09ce5528fafe"},
{file = "pydantic-1.10.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5389eb3b48a72da28c6e061a247ab224381435256eb541e175798483368fdd3"},
{file = "pydantic-1.10.18-cp38-cp38-win_amd64.whl", hash = "sha256:069b9c9fc645474d5ea3653788b544a9e0ccd3dca3ad8c900c4c6eac844b4620"},
{file = "pydantic-1.10.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80b982d42515632eb51f60fa1d217dfe0729f008e81a82d1544cc392e0a50ddf"},
{file = "pydantic-1.10.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aad8771ec8dbf9139b01b56f66386537c6fe4e76c8f7a47c10261b69ad25c2c9"},
{file = "pydantic-1.10.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941a2eb0a1509bd7f31e355912eb33b698eb0051730b2eaf9e70e2e1589cae1d"},
{file = "pydantic-1.10.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65f7361a09b07915a98efd17fdec23103307a54db2000bb92095457ca758d485"},
{file = "pydantic-1.10.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6951f3f47cb5ca4da536ab161ac0163cab31417d20c54c6de5ddcab8bc813c3f"},
{file = "pydantic-1.10.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7a4c5eec138a9b52c67f664c7d51d4c7234c5ad65dd8aacd919fb47445a62c86"},
{file = "pydantic-1.10.18-cp39-cp39-win_amd64.whl", hash = "sha256:49e26c51ca854286bffc22b69787a8d4063a62bf7d83dc21d44d2ff426108518"},
{file = "pydantic-1.10.18-py3-none-any.whl", hash = "sha256:06a189b81ffc52746ec9c8c007f16e5167c8b0a696e1a726369327e3db7b2a82"},
{file = "pydantic-1.10.18.tar.gz", hash = "sha256:baebdff1907d1d96a139c25136a9bb7d17e118f133a76a2ef3b845e831e3403a"},
]
[package.dependencies]
@@ -5064,4 +5072,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<3.13"
content-hash = "edaed8972a6e8dc5f6a5b58e4dd92df8a9985a03f0a45f086e5ae953e830d180"
content-hash = "0b367fa80501022efe43dc1beaa7f3da278fb64ffaddece72a0e88b09a0e53a2"
+6
View File
@@ -73,6 +73,7 @@ from prowler.providers.aws.models import AWSOutputOptions
from prowler.providers.azure.models import AzureOutputOptions
from prowler.providers.common.provider import Provider
from prowler.providers.common.quick_inventory import run_provider_quick_inventory
from prowler.providers.common.scan_inventory import run_prowler_scan_inventory
from prowler.providers.gcp.models import GCPOutputOptions
from prowler.providers.kubernetes.models import KubernetesOutputOptions
@@ -688,6 +689,11 @@ def prowler():
if checks_folder:
remove_custom_checks_module(checks_folder, provider)
# Run the quick inventory for the provider if available
if hasattr(args, "scan_inventory") and args.scan_inventory:
run_prowler_scan_inventory(checks_to_execute, args.provider)
sys.exit()
# If there are failed findings exit code 3, except if -z is input
if (
not args.ignore_exit_code_3
+1 -1
View File
@@ -11,7 +11,7 @@ from prowler.lib.logger import logger
timestamp = datetime.today()
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc)
prowler_version = "4.4.2"
prowler_version = "4.5.0"
html_logo_url = "https://github.com/prowler-cloud/prowler/"
square_logo_img = "https://prowler.com/wp-content/uploads/logo-html.png"
aws_logo = "https://user-images.githubusercontent.com/38561120/235953920-3e3fba08-0795-41dc-b480-9bea57db9f2e.png"
+5
View File
@@ -57,6 +57,11 @@ aws:
8088,
]
# AWS ECS Configuration
# aws.ecs_service_fargate_latest_platform_version
fargate_linux_latest_version: "1.4.0"
fargate_windows_latest_version: "1.0.0"
# AWS VPC Configuration (vpc_endpoint_connections_trust_boundaries, vpc_endpoint_services_allowed_principals_trust_boundaries)
# AWS SSM Configuration (aws.ssm_documents_set_as_public)
# Single account environment: No action required. The AWS account number will be automatically added by the checks.
+51 -39
View File
@@ -9,7 +9,6 @@ from py_ocsf_models.events.findings.detection_finding import (
from py_ocsf_models.events.findings.finding import ActivityID, FindingInformation
from py_ocsf_models.objects.account import Account, TypeID
from py_ocsf_models.objects.cloud import Cloud
from py_ocsf_models.objects.container import Container
from py_ocsf_models.objects.group import Group
from py_ocsf_models.objects.metadata import Metadata
from py_ocsf_models.objects.organization import Organization
@@ -37,7 +36,7 @@ class OCSF(Output):
- transform(findings: List[Finding]) -> None: Transforms the findings into the OCSF Detection Finding format.
- batch_write_data_to_file() -> None: Writes the findings to a file using the OCSF Detection Finding format using the `Output._file_descriptor`.
- get_account_type_id_by_provider(provider: str) -> TypeID: Returns the TypeID based on the provider.
- get_finding_status_id(status: str, muted: bool) -> StatusID: Returns the StatusID based on the status and muted values.
- get_finding_status_id(muted: bool) -> StatusID: Returns the StatusID based on the muted value.
References:
- OCSF: https://schema.ocsf.io/1.2.0/classes/detection_finding
@@ -59,21 +58,24 @@ class OCSF(Output):
finding_severity = getattr(
SeverityID, finding.severity.capitalize(), SeverityID.Unknown
)
finding_status = self.get_finding_status_id(
finding.status, finding.muted
)
finding_status = self.get_finding_status_id(finding.muted)
detection_finding = DetectionFinding(
message=finding.status_extended,
activity_id=finding_activity.value,
activity_name=finding_activity.name,
finding_info=FindingInformation(
created_time=finding.timestamp,
created_time_dt=finding.timestamp,
created_time=int(finding.timestamp.timestamp()),
desc=finding.description,
title=finding.check_title,
uid=finding.finding_uid,
name=finding.resource_name,
product_uid="prowler",
types=[finding.check_type],
),
event_time=finding.timestamp,
time_dt=finding.timestamp,
time=int(finding.timestamp.timestamp()),
remediation=Remediation(
desc=finding.remediation_recommendation_text,
references=list(
@@ -96,31 +98,51 @@ class OCSF(Output):
status_code=finding.status,
status_detail=finding.status_extended,
risk_details=finding.risk,
resources=[
ResourceDetails(
labels=unroll_dict_to_list(finding.resource_tags),
name=finding.resource_name,
uid=finding.resource_uid,
group=Group(name=finding.service_name),
type=finding.resource_type,
# TODO: this should be included only if using the Cloud profile
cloud_partition=finding.partition,
region=finding.region,
data={"details": finding.resource_details},
)
],
resources=(
[
ResourceDetails(
labels=unroll_dict_to_list(finding.resource_tags),
name=finding.resource_name,
uid=finding.resource_uid,
group=Group(name=finding.service_name),
type=finding.resource_type,
# TODO: this should be included only if using the Cloud profile
cloud_partition=finding.partition,
region=finding.region,
data={"details": finding.resource_details},
)
]
if finding.provider != "kubernetes"
else [
ResourceDetails(
labels=unroll_dict_to_list(finding.resource_tags),
name=finding.resource_name,
uid=finding.resource_uid,
group=Group(name=finding.service_name),
type=finding.resource_type,
data={"details": finding.resource_details},
namespace=finding.region.replace("namespace: ", ""),
)
]
),
metadata=Metadata(
event_code=finding.check_id,
product=Product(
uid="prowler",
name="Prowler",
vendor_name="Prowler",
version=finding.prowler_version,
),
profiles=(
["cloud", "datetime"]
if finding.provider != "kubernetes"
else ["container", "datetime"]
),
tenant_uid=finding.account_organization_uid,
),
type_uid=DetectionFindingTypeID.Create,
type_name=DetectionFindingTypeID.Create.name,
type_name=f"Detection Finding: {DetectionFindingTypeID.Create.name}",
unmapped={
"check_type": finding.check_type,
"related_url": finding.related_url,
"categories": finding.categories,
"depends_on": finding.depends_on,
@@ -129,26 +151,19 @@ class OCSF(Output):
"compliance": finding.compliance,
},
)
if finding.provider == "kubernetes":
detection_finding.container = Container(
name=finding.resource_name,
uid=finding.resource_uid,
)
# TODO: Get the PID of the namespace (we only have the name of the namespace)
# detection_finding.namespace_pid=,
else:
if finding.provider != "kubernetes":
detection_finding.cloud = Cloud(
account=Account(
name=finding.account_name,
type_id=cloud_account_type.value,
type=cloud_account_type.name,
type=cloud_account_type.name.replace("_", " "),
uid=finding.account_uid,
labels=unroll_dict_to_list(finding.account_tags),
),
org=Organization(
uid=finding.account_organization_uid,
name=finding.account_organization_name,
# TODO: add the org unit id and name
),
provider=finding.provider,
region=finding.region,
@@ -208,20 +223,17 @@ class OCSF(Output):
return type_id
@staticmethod
def get_finding_status_id(status: str, muted: bool) -> StatusID:
def get_finding_status_id(muted: bool) -> StatusID:
"""
Returns the StatusID based on the status and muted values.
Returns the StatusID based on the muted value.
Args:
status (str): The status value
muted (bool): The muted value
Returns:
StatusID: The StatusID based on the status and muted values
StatusID: The StatusID based on the muted value
"""
status_id = StatusID.Other
if status == "FAIL":
status_id = StatusID.New
status_id = StatusID.New
if muted:
status_id = StatusID.Suppressed
return status_id
+35 -5
View File
@@ -1,6 +1,8 @@
import datetime
from typing import Generator
from prowler.lib.check.check import execute, import_check, update_audit_metadata
from prowler.lib.check.utils import recover_checks_from_provider
from prowler.lib.logger import logger
from prowler.lib.outputs.finding import Finding
from prowler.providers.common.models import Audit_Metadata
@@ -18,8 +20,9 @@ class Scan:
_service_checks_completed: dict[str, set[str]]
_progress: float = 0.0
_findings: list = []
_duration: int = 0
def __init__(self, provider: Provider, checks_to_execute: list[str]):
def __init__(self, provider: Provider, checks_to_execute: list[str] = None):
"""
Scan is the class that executes the checks and yields the progress and the findings.
@@ -29,11 +32,31 @@ class Scan:
"""
self._provider = provider
# Remove duplicated checks and sort them
self._checks_to_execute = sorted(list(set(checks_to_execute)))
self._checks_to_execute = (
sorted(list(set(checks_to_execute)))
if checks_to_execute
else sorted(
[check[0] for check in recover_checks_from_provider(provider.type)]
)
)
self._number_of_checks_to_execute = len(checks_to_execute)
# TODO This should be done depending on the scan args (future feature)
# Discard threat detection checks
if "cloudtrail_threat_detection_enumeration" in self._checks_to_execute:
self._checks_to_execute.remove("cloudtrail_threat_detection_enumeration")
if (
"cloudtrail_threat_detection_privilege_escalation"
in self._checks_to_execute
):
self._checks_to_execute.remove(
"cloudtrail_threat_detection_privilege_escalation"
)
service_checks_to_execute = get_service_checks_to_execute(checks_to_execute)
self._number_of_checks_to_execute = len(self._checks_to_execute)
service_checks_to_execute = get_service_checks_to_execute(
self._checks_to_execute
)
service_checks_completed = dict()
self._service_checks_to_execute = service_checks_to_execute
@@ -61,6 +84,10 @@ class Scan:
self._number_of_checks_completed / self._number_of_checks_to_execute * 100
)
@property
def duration(self) -> int:
return self._duration
@property
def findings(self) -> list:
return self._findings
@@ -95,6 +122,8 @@ class Scan:
audit_progress=0,
)
start_time = datetime.datetime.now()
for check_name in checks_to_execute:
try:
# Recover service from check name
@@ -149,7 +178,6 @@ class Scan:
]
yield self.progress, findings
# If check does not exists in the provider or is from another provider
except ModuleNotFoundError:
logger.error(
@@ -159,6 +187,8 @@ class Scan:
logger.error(
f"{check_name} - {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
# Update the scan duration when all checks are completed
self._duration = int((datetime.datetime.now() - start_time).total_seconds())
except Exception as error:
logger.error(
f"{check_name} - {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -223,6 +223,7 @@
"ca-central-1",
"eu-central-1",
"eu-north-1",
"eu-south-2",
"eu-west-1",
"eu-west-2",
"sa-east-1",
@@ -1262,6 +1263,7 @@
"regions": {
"aws": [
"ap-northeast-1",
"ap-northeast-2",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
@@ -1272,6 +1274,7 @@
"eu-west-3",
"sa-east-1",
"us-east-1",
"us-east-2",
"us-west-2"
],
"aws-cn": [],
@@ -6925,6 +6928,7 @@
"eu-central-1",
"eu-north-1",
"eu-south-1",
"eu-south-2",
"eu-west-1",
"eu-west-2",
"eu-west-3",
@@ -7512,11 +7516,13 @@
"regions": {
"aws": [
"ap-northeast-1",
"ap-northeast-2",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-west-1",
"eu-west-2",
"eu-west-3",
@@ -7527,6 +7533,7 @@
],
"aws-cn": [],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
@@ -7688,6 +7695,7 @@
"eu-central-1",
"eu-north-1",
"eu-south-1",
"eu-south-2",
"eu-west-1",
"eu-west-2",
"eu-west-3",
@@ -10972,6 +10980,7 @@
"ap-east-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
@@ -100,6 +100,13 @@ def init_parser(self):
action="store_true",
help="Run Prowler Quick Inventory. The inventory will be stored in an output csv by default",
)
# AWS Scan Inventory
aws_scan_inventory_subparser = aws_parser.add_argument_group("Scan Inventory")
aws_scan_inventory_subparser.add_argument(
"--scan-inventory",
action="store_true",
help="Run Prowler Scan Inventory. The inventory will be stored in an output json file.",
)
# AWS Outputs
aws_outputs_subparser = aws_parser.add_argument_group("AWS Outputs to S3")
aws_outputs_bucket_parser = aws_outputs_subparser.add_mutually_exclusive_group()
@@ -1,4 +1,7 @@
from collections import deque
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
from typing import Any, Dict
from prowler.lib.logger import logger
from prowler.providers.aws.aws_provider import AwsProvider
@@ -101,3 +104,47 @@ class AWSService:
except Exception:
# Handle exceptions if necessary
pass # Replace 'pass' with any additional exception handling logic. Currently handled within the called function
def __to_dict__(self, seen=None) -> Dict[str, Any]:
if seen is None:
seen = set()
def convert_value(value):
if isinstance(value, (AwsProvider,)):
return {}
if isinstance(value, datetime):
return value.isoformat() # Convert datetime to ISO 8601 string
elif isinstance(value, deque):
return [convert_value(item) for item in value]
elif isinstance(value, list):
return [convert_value(item) for item in value]
elif isinstance(value, tuple):
return tuple(convert_value(item) for item in value)
elif isinstance(value, dict):
# Ensure keys are strings and values are processed
return {
convert_value(str(k)): convert_value(v) for k, v in value.items()
}
elif hasattr(value, "__dict__"):
obj_id = id(value)
if obj_id in seen:
return None # Avoid infinite recursion
seen.add(obj_id)
return {key: convert_value(val) for key, val in value.__dict__.items()}
else:
return value # Handle basic types and non-serializable objects
return {
key: convert_value(value)
for key, value in self.__dict__.items()
if key
not in [
"audit_config",
"provider",
"session",
"regional_clients",
"client",
"thread_pool",
"fixer_config",
]
}
@@ -0,0 +1,6 @@
from prowler.providers.aws.services.autoscaling.autoscaling_service import (
ApplicationAutoScaling,
)
from prowler.providers.common.provider import Provider
applicationautoscaling_client = ApplicationAutoScaling(Provider.get_global_provider())
@@ -5,7 +5,6 @@ from prowler.lib.scan_filters.scan_filters import is_resource_filtered
from prowler.providers.aws.lib.service.service import AWSService
################## AutoScaling
class AutoScaling(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
@@ -74,6 +73,49 @@ class AutoScaling(AWSService):
)
# Global list for service namespaces needed for Describe Scalable Targets
SERVICE_NAMESPACES = ["dynamodb"]
class ApplicationAutoScaling(AWSService):
def __init__(self, provider):
super().__init__("application-autoscaling", provider)
self.scalable_targets = []
self.__threading_call__(self._describe_scalable_targets)
def _describe_scalable_targets(self, regional_client):
logger.info("ApplicationAutoScaling - Describing Scalable Targets...")
try:
describe_scalable_targets_paginator = regional_client.get_paginator(
"describe_scalable_targets"
)
for service_namespace in SERVICE_NAMESPACES:
logger.info(f"Processing ServiceNamespace: {service_namespace}")
for page in describe_scalable_targets_paginator.paginate(
ServiceNamespace=service_namespace
):
for target in page.get("ScalableTargets", []):
if not self.audit_resources or (
is_resource_filtered(
target["ScalableTargetARN"],
self.audit_resources,
)
):
self.scalable_targets.append(
ScalableTarget(
arn=target.get("ScalableTargetARN", ""),
resource_id=target.get("ResourceId"),
service_namespace=target.get("ServiceNamespace"),
scalable_dimension=target.get("ScalableDimension"),
region=regional_client.region,
)
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class LaunchConfiguration(BaseModel):
arn: str
name: str
@@ -88,3 +130,11 @@ class Group(BaseModel):
region: str
availability_zones: list
tags: list = []
class ScalableTarget(BaseModel):
arn: str
resource_id: str
service_namespace: str
scalable_dimension: str
region: str
@@ -1,7 +1,7 @@
{
"Provider": "aws",
"CheckID": "cloudfront_distributions_s3_origin_non_existent_bucket",
"CheckTitle": "CloudFront distributions should not point to non-existent S3 origins without static website hosting.",
"CheckTitle": "CloudFront distributions should not point to non-existent S3 origins.",
"CheckType": [
"Software and Configuration Checks/Industry and Regulatory Standards/NIST 800-53 Controls"
],
@@ -10,7 +10,7 @@
"ResourceIdTemplate": "arn:partition:cloudfront:region:account-id:distribution/resource-id",
"Severity": "high",
"ResourceType": "AwsCloudFrontDistribution",
"Description": "This control checks whether Amazon CloudFront distributions are pointing to non-existent Amazon S3 origins without static website hosting. The control fails if the origin is configured to point to a non-existent bucket.",
"Description": "This control checks whether Amazon CloudFront distributions are pointing to non-existent Amazon S3 origins. The control fails if the origin is configured to point to a non-existent bucket.",
"Risk": "Pointing a CloudFront distribution to a non-existent S3 bucket can allow malicious actors to create the bucket and potentially serve unauthorized content through your distribution, leading to security and integrity issues.",
"RelatedUrl": "https://docs.aws.amazon.com/whitepapers/latest/secure-content-delivery-amazon-cloudfront/s3-origin-with-cloudfront.html",
"Remediation": {
@@ -19,10 +19,9 @@ class cloudfront_distributions_s3_origin_non_existent_bucket(Check):
non_existent_buckets = []
for origin in distribution.origins:
if origin.s3_origin_config:
bucket_name = origin.domain_name.split(".")[0]
if not s3_client._head_bucket(bucket_name):
non_existent_buckets.append(bucket_name)
bucket_name = origin.domain_name.split(".")[0]
if not s3_client._head_bucket(bucket_name):
non_existent_buckets.append(bucket_name)
if non_existent_buckets:
report.status = "FAIL"
@@ -15,24 +15,24 @@ class cloudwatch_changes_to_network_acls_alarm_configured(Check):
def execute(self):
pattern = r"\$\.eventName\s*=\s*.?CreateNetworkAcl.+\$\.eventName\s*=\s*.?CreateNetworkAclEntry.+\$\.eventName\s*=\s*.?DeleteNetworkAcl.+\$\.eventName\s*=\s*.?DeleteNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclEntry.+\$\.eventName\s*=\s*.?ReplaceNetworkAclAssociation.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_changes_to_network_gateways_alarm_configured(Check):
def execute(self):
pattern = r"\$\.eventName\s*=\s*.?CreateCustomerGateway.+\$\.eventName\s*=\s*.?DeleteCustomerGateway.+\$\.eventName\s*=\s*.?AttachInternetGateway.+\$\.eventName\s*=\s*.?CreateInternetGateway.+\$\.eventName\s*=\s*.?DeleteInternetGateway.+\$\.eventName\s*=\s*.?DetachInternetGateway.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_changes_to_network_route_tables_alarm_configured(Check):
def execute(self):
pattern = r"\$\.eventSource\s*=\s*.?ec2.amazonaws.com.+\$\.eventName\s*=\s*.?CreateRoute.+\$\.eventName\s*=\s*.?CreateRouteTable.+\$\.eventName\s*=\s*.?ReplaceRoute.+\$\.eventName\s*=\s*.?ReplaceRouteTableAssociation.+\$\.eventName\s*=\s*.?DeleteRouteTable.+\$\.eventName\s*=\s*.?DeleteRoute.+\$\.eventName\s*=\s*.?DisassociateRouteTable.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_changes_to_vpcs_alarm_configured(Check):
def execute(self):
pattern = r"\$\.eventName\s*=\s*.?CreateVpc.+\$\.eventName\s*=\s*.?DeleteVpc.+\$\.eventName\s*=\s*.?ModifyVpcAttribute.+\$\.eventName\s*=\s*.?AcceptVpcPeeringConnection.+\$\.eventName\s*=\s*.?CreateVpcPeeringConnection.+\$\.eventName\s*=\s*.?DeleteVpcPeeringConnection.+\$\.eventName\s*=\s*.?RejectVpcPeeringConnection.+\$\.eventName\s*=\s*.?AttachClassicLinkVpc.+\$\.eventName\s*=\s*.?DetachClassicLinkVpc.+\$\.eventName\s*=\s*.?DisableVpcClassicLink.+\$\.eventName\s*=\s*.?EnableVpcClassicLink.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -16,6 +16,7 @@ class cloudwatch_cross_account_sharing_disabled(Check):
if role.name == "CloudWatch-CrossAccountSharingRole":
report.resource_arn = role.arn
report.resource_id = role.name
report.resource_tags = role.tags
report.status = "FAIL"
report.status_extended = (
"CloudWatch has allowed cross-account sharing."
@@ -24,6 +24,7 @@ class cloudwatch_log_group_no_secrets_in_logs(Check):
report.region = log_group.region
report.resource_id = log_group.name
report.resource_arn = log_group.arn
report.resource_tags = log_group.tags
log_group_secrets = []
if log_group.log_streams:
for log_stream_name in log_group.log_streams:
@@ -17,24 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_aws_config_configuration_change
def execute(self):
pattern = r"\$\.eventSource\s*=\s*.?config.amazonaws.com.+\$\.eventName\s*=\s*.?StopConfigurationRecorder.+\$\.eventName\s*=\s*.?DeleteDeliveryChannel.+\$\.eventName\s*=\s*.?PutDeliveryChannel.+\$\.eventName\s*=\s*.?PutConfigurationRecorder.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -17,24 +17,24 @@ class cloudwatch_log_metric_filter_and_alarm_for_cloudtrail_configuration_change
def execute(self):
pattern = r"\$\.eventName\s*=\s*.?CreateTrail.+\$\.eventName\s*=\s*.?UpdateTrail.+\$\.eventName\s*=\s*.?DeleteTrail.+\$\.eventName\s*=\s*.?StartLogging.+\$\.eventName\s*=\s*.?StopLogging.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_log_metric_filter_authentication_failures(Check):
def execute(self):
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.errorMessage\s*=\s*.?Failed authentication.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_log_metric_filter_aws_organizations_changes(Check):
def execute(self):
pattern = r"\$\.eventSource\s*=\s*.?organizations\.amazonaws\.com.+\$\.eventName\s*=\s*.?AcceptHandshake.+\$\.eventName\s*=\s*.?AttachPolicy.+\$\.eventName\s*=\s*.?CancelHandshake.+\$\.eventName\s*=\s*.?CreateAccount.+\$\.eventName\s*=\s*.?CreateOrganization.+\$\.eventName\s*=\s*.?CreateOrganizationalUnit.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeclineHandshake.+\$\.eventName\s*=\s*.?DeleteOrganization.+\$\.eventName\s*=\s*.?DeleteOrganizationalUnit.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?EnableAllFeatures.+\$\.eventName\s*=\s*.?EnablePolicyType.+\$\.eventName\s*=\s*.?InviteAccountToOrganization.+\$\.eventName\s*=\s*.?LeaveOrganization.+\$\.eventName\s*=\s*.?DetachPolicy.+\$\.eventName\s*=\s*.?DisablePolicyType.+\$\.eventName\s*=\s*.?MoveAccount.+\$\.eventName\s*=\s*.?RemoveAccountFromOrganization.+\$\.eventName\s*=\s*.?UpdateOrganizationalUnit.+\$\.eventName\s*=\s*.?UpdatePolicy.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_log_metric_filter_disable_or_scheduled_deletion_of_kms_cmk(Chec
def execute(self):
pattern = r"\$\.eventSource\s*=\s*.?kms.amazonaws.com.+\$\.eventName\s*=\s*.?DisableKey.+\$\.eventName\s*=\s*.?ScheduleKeyDeletion.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,25 +15,24 @@ class cloudwatch_log_metric_filter_for_s3_bucket_policy_changes(Check):
def execute(self):
pattern = r"\$\.eventSource\s*=\s*.?s3.amazonaws.com.+\$\.eventName\s*=\s*.?PutBucketAcl.+\$\.eventName\s*=\s*.?PutBucketPolicy.+\$\.eventName\s*=\s*.?PutBucketCors.+\$\.eventName\s*=\s*.?PutBucketLifecycle.+\$\.eventName\s*=\s*.?PutBucketReplication.+\$\.eventName\s*=\s*.?DeleteBucketPolicy.+\$\.eventName\s*=\s*.?DeleteBucketCors.+\$\.eventName\s*=\s*.?DeleteBucketLifecycle.+\$\.eventName\s*=\s*.?DeleteBucketReplication.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_log_metric_filter_policy_changes(Check):
def execute(self):
pattern = r"\$\.eventName\s*=\s*.?DeleteGroupPolicy.+\$\.eventName\s*=\s*.?DeleteRolePolicy.+\$\.eventName\s*=\s*.?DeleteUserPolicy.+\$\.eventName\s*=\s*.?PutGroupPolicy.+\$\.eventName\s*=\s*.?PutRolePolicy.+\$\.eventName\s*=\s*.?PutUserPolicy.+\$\.eventName\s*=\s*.?CreatePolicy.+\$\.eventName\s*=\s*.?DeletePolicy.+\$\.eventName\s*=\s*.?CreatePolicyVersion.+\$\.eventName\s*=\s*.?DeletePolicyVersion.+\$\.eventName\s*=\s*.?AttachRolePolicy.+\$\.eventName\s*=\s*.?DetachRolePolicy.+\$\.eventName\s*=\s*.?AttachUserPolicy.+\$\.eventName\s*=\s*.?DetachUserPolicy.+\$\.eventName\s*=\s*.?AttachGroupPolicy.+\$\.eventName\s*=\s*.?DetachGroupPolicy.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_log_metric_filter_root_usage(Check):
def execute(self):
pattern = r"\$\.userIdentity\.type\s*=\s*.?Root.+\$\.userIdentity\.invokedBy NOT EXISTS.+\$\.eventType\s*!=\s*.?AwsServiceEvent.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_log_metric_filter_security_group_changes(Check):
def execute(self):
pattern = r"\$\.eventName\s*=\s*.?AuthorizeSecurityGroupIngress.+\$\.eventName\s*=\s*.?AuthorizeSecurityGroupEgress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupIngress.+\$\.eventName\s*=\s*.?RevokeSecurityGroupEgress.+\$\.eventName\s*=\s*.?CreateSecurityGroup.+\$\.eventName\s*=\s*.?DeleteSecurityGroup.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_log_metric_filter_sign_in_without_mfa(Check):
def execute(self):
pattern = r"\$\.eventName\s*=\s*.?ConsoleLogin.+\$\.additionalEventData\.MFAUsed\s*!=\s*.?Yes.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -15,24 +15,24 @@ class cloudwatch_log_metric_filter_unauthorized_api_calls(Check):
def execute(self):
pattern = r"\$\.errorCode\s*=\s*.?\*UnauthorizedOperation.+\$\.errorCode\s*=\s*.?AccessDenied\*.?"
findings = []
if (
cloudtrail_client.trails is not None
and logs_client.metric_filters is not None
and cloudwatch_client.metric_alarms is not None
):
report = Check_Report_AWS(self.metadata())
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
report,
)
report = check_cloudwatch_log_metric_filter(
pattern,
cloudtrail_client.trails,
logs_client.metric_filters,
cloudwatch_client.metric_alarms,
self.metadata(),
)
if report is not None:
if report == Check_Report_AWS(self.metadata()):
report.status = "FAIL"
report.status_extended = "No CloudWatch log groups found with metric filters or alarms associated."
report.region = logs_client.region
report.resource_id = logs_client.audited_account
report.resource_arn = logs_client.log_group_arn_template
report.resource_tags = []
findings.append(report)
return findings
@@ -82,10 +82,10 @@ class Logs(AWSService):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.log_group_arn_template = f"arn:{self.audited_partition}:logs:{self.region}:{self.audited_account}:log-group"
self.metric_filters = []
self.log_groups = []
self.__threading_call__(self._describe_metric_filters)
self.__threading_call__(self._describe_log_groups)
self.metric_filters = []
self.__threading_call__(self._describe_metric_filters)
if self.log_groups:
if (
"cloudwatch_log_group_no_secrets_in_logs"
@@ -111,13 +111,20 @@ class Logs(AWSService):
):
if self.metric_filters is None:
self.metric_filters = []
log_group = None
for lg in self.log_groups:
if lg.name == filter["logGroupName"]:
log_group = lg
break
self.metric_filters.append(
MetricFilter(
arn=arn,
name=filter["filterName"],
metric=filter["metricTransformations"][0]["metricName"],
pattern=filter.get("filterPattern", ""),
log_group=filter["logGroupName"],
log_group=log_group,
region=regional_client.region,
)
)
@@ -127,7 +134,7 @@ class Logs(AWSService):
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
if not self.metric_filters:
self.metric_filters = []
self.metric_filters = None
else:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -242,15 +249,6 @@ class MetricAlarm(BaseModel):
tags: Optional[list] = []
class MetricFilter(BaseModel):
arn: str
name: str
metric: str
pattern: str
log_group: str
region: str
class LogGroup(BaseModel):
arn: str
name: str
@@ -264,6 +262,15 @@ class LogGroup(BaseModel):
tags: Optional[list] = []
class MetricFilter(BaseModel):
arn: str
name: str
metric: str
pattern: str
log_group: Optional[LogGroup]
region: str
def convert_to_cloudwatch_timestamp_format(epoch_time):
date_time = datetime.fromtimestamp(
epoch_time / 1000, datetime.now(timezone.utc).astimezone().tzinfo
@@ -8,28 +8,34 @@ def check_cloudwatch_log_metric_filter(
trails: list,
metric_filters: list,
metric_alarms: list,
report: Check_Report_AWS,
metadata: dict,
):
report = None
# 1. Iterate for CloudWatch Log Group in CloudTrail trails
log_groups = []
if trails is not None:
if trails is not None and metric_filters is not None and metric_alarms is not None:
report = Check_Report_AWS(metadata)
for trail in trails.values():
if trail.log_group_arn:
log_groups.append(trail.log_group_arn.split(":")[6])
# 2. Describe metric filters for previous log groups
for metric_filter in metric_filters:
if metric_filter.log_group in log_groups:
if re.search(metric_filter_pattern, metric_filter.pattern, flags=re.DOTALL):
report.resource_id = metric_filter.log_group
report.resource_arn = metric_filter.arn
report.region = metric_filter.region
report.status = "FAIL"
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} but no alarms associated."
# 3. Check if there is an alarm for the metric
for alarm in metric_alarms:
if alarm.metric == metric_filter.metric:
report.status = "PASS"
report.status_extended = f"CloudWatch log group {metric_filter.log_group} found with metric filter {metric_filter.name} and alarms set."
break
if metric_filter.log_group.name in log_groups and re.search(
metric_filter_pattern, metric_filter.pattern, flags=re.DOTALL
):
report.resource_id = metric_filter.log_group.name
report.resource_arn = metric_filter.log_group.arn
report.region = metric_filter.log_group.region
report.resource_tags = getattr(metric_filter.log_group, "tags", [])
report.status = "FAIL"
report.status_extended = f"CloudWatch log group {metric_filter.log_group.name} found with metric filter {metric_filter.name} but no alarms associated."
# 3. Check if there is an alarm for the metric
for alarm in metric_alarms:
if alarm.metric == metric_filter.metric:
report.status = "PASS"
report.status_extended = f"CloudWatch log group {metric_filter.log_group.name} found with metric filter {metric_filter.name} and alarms set."
break
if report.status == "PASS":
break
return report
@@ -16,7 +16,7 @@ class cognito_user_pool_waf_acl_attached(Check):
report.status_extended = (
f"Cognito User Pool {pool.name} is not associated with a WAF Web ACL."
)
for acl in wafv2_client.web_acls:
for acl in wafv2_client.web_acls.values():
if pool.arn in acl.user_pools:
report.status = "PASS"
report.status_extended = f"Cognito User Pool {pool.name} is associated with the WAF Web ACL {acl.name}."
@@ -11,7 +11,6 @@ from prowler.providers.aws.lib.service.service import AWSService
class DynamoDB(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.tables = {}
self.__threading_call__(self._list_tables)
@@ -49,6 +48,9 @@ class DynamoDB(AWSService):
properties = regional_client.describe_table(TableName=table.name)[
"Table"
]
table.billing_mode = properties.get("BillingModeSummary", {}).get(
"BillingMode", "PROVISIONED"
)
if "SSEDescription" in properties:
if "SSEType" in properties["SSEDescription"]:
table.encryption_type = properties["SSEDescription"]["SSEType"]
@@ -152,7 +154,6 @@ class DynamoDB(AWSService):
class DAX(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.clusters = []
self.__threading_call__(self._describe_clusters)
@@ -217,6 +218,7 @@ class DAX(AWSService):
class Table(BaseModel):
name: str
billing_mode: str = "PROVISIONED"
encryption_type: Optional[str]
kms_arn: Optional[str]
pitr: bool = False
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "dynamodb_table_autoscaling_enabled",
"CheckTitle": "Check if DynamoDB tables automatically scale capacity with demand.",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "dynamodb",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:dynamodb:region:account-id:table/table-name",
"Severity": "medium",
"ResourceType": "AwsDynamoDbTable",
"Description": "This check ensures that DynamoDB tables can scale their read and write capacity as needed, either using on-demand capacity mode or provisioned mode with auto scaling configured.",
"Risk": "If DynamoDB tables do not automatically scale capacity with demand, they may experience throttling exceptions, leading to reduced availability and performance of applications.",
"RelatedUrl": "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.Console.html#AutoScaling.Console.ExistingTable",
"Remediation": {
"Code": {
"CLI": "aws dynamodb update-table --table-name <table-name> --billing-mode PAY_PER_REQUEST",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/dynamodb-controls.html#dynamodb-1",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable DynamoDB automatic scaling on existing tables by configuring on-demand capacity mode or provisioned mode with auto scaling.",
"Url": "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.Console.html#AutoScaling.Console.ExistingTable"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,67 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.autoscaling.applicationautoscaling_client import (
applicationautoscaling_client,
)
from prowler.providers.aws.services.dynamodb.dynamodb_client import dynamodb_client
class dynamodb_table_autoscaling_enabled(Check):
def execute(self):
findings = []
scalable_targets = applicationautoscaling_client.scalable_targets
dynamodb_scalable_targets = [
target
for target in scalable_targets
if target.service_namespace == "dynamodb"
and target.resource_id.startswith("table/")
]
autoscaling_mapping = {}
for target in dynamodb_scalable_targets:
table_name = target.resource_id.split("/")[1]
if table_name not in autoscaling_mapping:
autoscaling_mapping[table_name] = {}
autoscaling_mapping[table_name][target.scalable_dimension] = target
for table_arn, table in dynamodb_client.tables.items():
report = Check_Report_AWS(self.metadata())
report.region = table.region
report.resource_id = table.name
report.resource_arn = table_arn
report.resource_tags = table.tags
report.status = "PASS"
report.status_extended = (
f"DynamoDB table {table.name} automatically scales capacity on demand."
)
if table.billing_mode == "PROVISIONED":
read_autoscaling = False
write_autoscaling = False
if table.name in autoscaling_mapping:
if (
"dynamodb:table:ReadCapacityUnits"
in autoscaling_mapping[table.name]
):
read_autoscaling = True
if (
"dynamodb:table:WriteCapacityUnits"
in autoscaling_mapping[table.name]
):
write_autoscaling = True
if read_autoscaling and write_autoscaling:
report.status = "PASS"
report.status_extended = f"DynamoDB table {table.name} is in provisioned mode with auto scaling enabled for both read and write capacity units."
else:
missing_autoscaling = []
if not read_autoscaling:
missing_autoscaling.append("read")
if not write_autoscaling:
missing_autoscaling.append("write")
if missing_autoscaling:
report.status = "FAIL"
report.status_extended = f"DynamoDB table {table.name} is in provisioned mode without auto scaling enabled for {', '.join(missing_autoscaling)}."
findings.append(report)
return findings
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "ecs_cluster_container_insights_enabled",
"CheckTitle": "ECS clusters should use Container Insights",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "ecs",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:cluster/{cluster-name}",
"Severity": "medium",
"ResourceType": "AwsEcsCluster",
"Description": "This control checks if ECS clusters use Container Insights. This control fails if Container Insights are not set up for a cluster.",
"Risk": "Without Container Insights, important performance metrics and diagnostic information from containerized applications may not be captured, which can hinder monitoring and troubleshooting.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-container-insights-enabled.html",
"Remediation": {
"Code": {
"CLI": "aws ecs update-cluster-settings --cluster <cluster-name> --settings name=containerInsights,value=enabled",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-12",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable Container Insights for your ECS clusters to collect and monitor key performance metrics and diagnostic data from your containers.",
"Url": "https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/deploy-container-insights-ECS-cluster.html"
}
},
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,27 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ecs.ecs_client import ecs_client
class ecs_cluster_container_insights_enabled(Check):
def execute(self):
findings = []
for cluster in ecs_client.clusters.values():
report = Check_Report_AWS(self.metadata())
report.region = cluster.region
report.resource_id = cluster.name
report.resource_arn = cluster.arn
report.resource_tags = cluster.tags
report.status = "FAIL"
report.status_extended = (
f"ECS cluster {cluster.name} does not have container insights enabled."
)
if cluster.settings:
for setting in cluster.settings:
if (
setting["name"] == "containerInsights"
and setting["value"] == "enabled"
):
report.status = "PASS"
report.status_extended = f"ECS cluster {cluster.name} has container insights enabled."
findings.append(report)
return findings
@@ -119,6 +119,9 @@ class ECS(AWSService):
.get("assignPublicIp", "DISABLED")
== "ENABLED"
),
launch_type=service_desc.get("launchType", ""),
platform_version=service_desc.get("platformVersion", ""),
platform_family=service_desc.get("platformFamily", ""),
tags=service_desc.get("tags", []),
)
cluster.services[service_arn] = service_obj
@@ -157,6 +160,7 @@ class ECS(AWSService):
"TAGS",
],
)
cluster.settings = response["clusters"][0].get("settings", [])
cluster.tags = response["clusters"][0].get("tags", [])
except Exception as error:
logger.error(
@@ -193,6 +197,9 @@ class Service(BaseModel):
name: str
arn: str
region: str
launch_type: str = ""
platform_version: Optional[str]
platform_family: Optional[str]
assign_public_ip: Optional[bool]
tags: Optional[list] = []
@@ -202,4 +209,5 @@ class Cluster(BaseModel):
arn: str
region: str
services: dict = {}
settings: Optional[list] = []
tags: Optional[list] = []
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "ecs_service_fargate_latest_platform_version",
"CheckTitle": "ECS Fargate services should run on the latest Fargate platform version",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "ecs",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:ecs:{region}:{account-id}:service/{service-name}",
"Severity": "medium",
"ResourceType": "AwsEcsService",
"Description": "This control checks if Amazon ECS Fargate services are running the latest Fargate platform version. The control fails if the platform version is not the latest.",
"Risk": "Not running the latest Fargate platform version may expose your services to security vulnerabilities and bugs that are resolved in newer versions.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/ecs-fargate-latest-platform-version.html",
"Remediation": {
"Code": {
"CLI": "aws ecs update-service --cluster <cluster-name> --service <service-name> --platform-version LATEST",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/ecs-controls.html#ecs-10",
"Terraform": ""
},
"Recommendation": {
"Text": "Update your ECS Fargate services to the latest platform version to ensure they are running in a secure and optimized environment.",
"Url": "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html"
}
},
"Categories": [
"vulnerabilities"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,38 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ecs.ecs_client import ecs_client
class ecs_service_fargate_latest_platform_version(Check):
def execute(self):
findings = []
for service in ecs_client.services.values():
if service.launch_type == "FARGATE":
report = Check_Report_AWS(self.metadata())
report.region = service.region
report.resource_id = service.name
report.resource_arn = service.arn
report.resource_tags = service.tags
fargate_latest_linux_version = ecs_client.audit_config.get(
"fargate_linux_latest_version", "1.4.0"
)
fargate_latest_windows_version = ecs_client.audit_config.get(
"fargate_windows_latest_version", "1.0.0"
)
report.status = "PASS"
report.status_extended = f"ECS Service {service.name} is using latest FARGATE {service.platform_family} version {fargate_latest_linux_version if service.platform_family == 'Linux' else fargate_latest_windows_version}."
if (
service.platform_version != "LATEST"
and (
service.platform_family == "Linux"
and service.platform_version != fargate_latest_linux_version
)
or (
service.platform_family == "Windows"
and service.platform_version != fargate_latest_windows_version
)
):
report.status = "FAIL"
report.status_extended = f"ECS Service {service.name} is not using latest FARGATE {service.platform_family} version {fargate_latest_linux_version if service.platform_family == 'Linux' else fargate_latest_windows_version}, currently using {service.platform_version}."
findings.append(report)
return findings
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "efs_access_point_enforce_root_directory",
"CheckTitle": "EFS access points should enforce a root directory",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "efs",
"SubServiceName": "access-point",
"ResourceIdTemplate": "arn:aws:elasticfilesystem:{region}:{account-id}:access-point/{access-point-id}",
"Severity": "medium",
"ResourceType": "AwsEfsAccessPoint",
"Description": "This control checks if Amazon EFS access points are configured to enforce a root directory. The control fails if the value of Path is set to / (the default root directory of the file system).",
"Risk": "Access points without enforced root directories can potentially expose the entire file system's root directory to clients, which may result in unauthorized access.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/efs-access-point-enforce-root-directory.html",
"Remediation": {
"Code": {
"CLI": "aws efs update-access-point --access-point-id <access-point-id> --root-directory Path=<non-root-directory-path>",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/efs-controls.html#efs-3",
"Terraform": ""
},
"Recommendation": {
"Text": "Update the EFS access point to enforce a non-root directory. This ensures clients can only access a specified subdirectory.",
"Url": "https://docs.aws.amazon.com/efs/latest/ug/enforce-root-directory-access-point.html"
}
},
"Categories": [
"vulnerabilities"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,25 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.efs.efs_client import efs_client
class efs_access_point_enforce_root_directory(Check):
def execute(self):
findings = []
for fs in efs_client.filesystems.values():
if fs.access_points:
report = Check_Report_AWS(self.metadata())
report.region = fs.region
report.resource_id = fs.id
report.resource_arn = fs.arn
report.resource_tags = fs.tags
report.status = "PASS"
report.status_extended = f"EFS {fs.id} does not have any access point allowing access to the root directory."
access_points = []
for access_point in fs.access_points:
if access_point.root_directory_path == "/":
access_points.append(access_point)
if access_points:
report.status = "FAIL"
report.status_extended = f"EFS {fs.id} has access points which allow access to the root directory: {', '.join([ap.id for ap in access_points])}."
findings.append(report)
return findings
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "efs_access_point_enforce_user_identity",
"CheckTitle": "EFS access points should enforce a user identity",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "efs",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:elasticfilesystem:{region}:{account-id}:access-point/{access-point-id}",
"Severity": "medium",
"ResourceType": "AwsEfsAccessPoint",
"Description": "This control checks whether Amazon EFS access points are configured to enforce a user identity. This control fails if a POSIX user identity is not defined while creating the EFS access point.",
"Risk": "Without enforcing a user identity, access to the file system can become less controlled, leading to potential unauthorized access or modifications.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/efs-access-point-enforce-user-identity.html",
"Remediation": {
"Code": {
"CLI": "aws efs create-access-point --file-system-id <file-system-id> --posix-user Uid=<uid>,Gid=<gid>",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/efs-controls.html#efs-4",
"Terraform": ""
},
"Recommendation": {
"Text": "Create or update the EFS access point to enforce a user identity using POSIX attributes for Uid and Gid.",
"Url": "https://docs.aws.amazon.com/efs/latest/ug/enforce-identity-access-points.html"
}
},
"Categories": [
"trustboundaries"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,28 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.efs.efs_client import efs_client
class efs_access_point_enforce_user_identity(Check):
def execute(self):
findings = []
for fs in efs_client.filesystems.values():
if fs.access_points:
report = Check_Report_AWS(self.metadata())
report.region = fs.region
report.resource_id = fs.id
report.resource_arn = fs.arn
report.resource_tags = fs.tags
report.status = "PASS"
report.status_extended = (
f"EFS {fs.id} has all access points with defined POSIX user."
)
access_points = []
for access_point in fs.access_points:
if not access_point.posix_user:
access_points.append(access_point)
if access_points:
report.status = "FAIL"
report.status_extended = f"EFS {fs.id} has access points with no POSIX user: {', '.join([ap.id for ap in access_points])}."
findings.append(report)
return findings
@@ -5,7 +5,7 @@ from prowler.providers.aws.services.efs.efs_client import efs_client
class efs_encryption_at_rest_enabled(Check):
def execute(self):
findings = []
for fs in efs_client.filesystems:
for fs in efs_client.filesystems.values():
report = Check_Report_AWS(self.metadata())
report.region = fs.region
report.resource_id = fs.id
@@ -5,7 +5,7 @@ from prowler.providers.aws.services.efs.efs_client import efs_client
class efs_have_backup_enabled(Check):
def execute(self):
findings = []
for fs in efs_client.filesystems:
for fs in efs_client.filesystems.values():
report = Check_Report_AWS(self.metadata())
report.region = fs.region
report.resource_id = fs.id
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "efs_mount_target_not_publicly_accessible",
"CheckTitle": "EFS mount targets should not be publicly accessible",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "efs",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:elasticfilesystem:{region}:{account-id}:file-system/{filesystem-id}",
"Severity": "medium",
"ResourceType": "AwsEfsFileSystem",
"Description": "This control checks whether an Amazon EFS mount target is associated with a public subnet since it can be accessed from the internet.",
"Risk": "Mount targets in public subnets may expose your EFS to unauthorized access or attacks.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/efs-mount-target-public-accessible.html",
"Remediation": {
"Code": {
"CLI": "aws efs create-mount-target --file-system-id <filesystem-id> --subnet-id <private-subnet-id> --security-groups <sg-ids>",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/efs-controls.html#efs-6",
"Terraform": ""
},
"Recommendation": {
"Text": "Recreate the EFS mount target in a private subnet to ensure it is not publicly accessible.",
"Url": "https://docs.aws.amazon.com/efs/latest/ug/accessing-fs.html"
}
},
"Categories": [
"internet-exposed"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,28 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.efs.efs_client import efs_client
from prowler.providers.aws.services.vpc.vpc_client import vpc_client
class efs_mount_target_not_publicly_accessible(Check):
def execute(self):
findings = []
for fs in efs_client.filesystems.values():
report = Check_Report_AWS(self.metadata())
report.region = fs.region
report.resource_id = fs.id
report.resource_arn = fs.arn
report.resource_tags = fs.tags
report.status = "PASS"
report.status_extended = (
f"EFS {fs.id} does not have any public mount targets."
)
mount_targets = []
for mt in fs.mount_targets:
if vpc_client.vpc_subnets[mt.subnet_id].public:
mount_targets.append(mt)
if mount_targets:
report.status = "FAIL"
report.status_extended = f"EFS {fs.id} has public mount targets: {', '.join([mt.id for mt in mount_targets])}"
findings.append(report)
return findings
@@ -6,7 +6,7 @@ from prowler.providers.aws.services.iam.lib.policy import is_policy_public
class efs_not_publicly_accessible(Check):
def execute(self):
findings = []
for fs in efs_client.filesystems:
for fs in efs_client.filesystems.values():
report = Check_Report_AWS(self.metadata())
report.region = fs.region
report.resource_id = fs.id
+119 -33
View File
@@ -14,9 +14,13 @@ class EFS(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.filesystems = []
self.filesystems = {}
self.__threading_call__(self._describe_file_systems)
self._describe_file_system_policies()
self.__threading_call__(
self._describe_file_system_policies, self.filesystems.values()
)
self.__threading_call__(self._describe_mount_targets, self.filesystems.values())
self.__threading_call__(self._describe_access_points, self.filesystems.values())
def _describe_file_systems(self, regional_client):
logger.info("EFS - Describing file systems...")
@@ -31,55 +35,137 @@ class EFS(AWSService):
if not self.audit_resources or (
is_resource_filtered(efs_arn, self.audit_resources)
):
self.filesystems.append(
FileSystem(
id=efs_id,
arn=efs_arn,
region=regional_client.region,
policy=None,
backup_policy=None,
encrypted=efs["Encrypted"],
tags=efs.get("Tags"),
)
self.filesystems[efs_arn] = FileSystem(
id=efs_id,
arn=efs_arn,
region=regional_client.region,
encrypted=efs["Encrypted"],
tags=efs.get("Tags"),
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _describe_file_system_policies(self):
def _describe_file_system_policies(self, filesystem):
logger.info("EFS - Describing file system policies...")
try:
for filesystem in self.filesystems:
for region, client in self.regional_clients.items():
if filesystem.region == region:
try:
filesystem.backup_policy = client.describe_backup_policy(
FileSystemId=filesystem.id
)["BackupPolicy"]["Status"]
except ClientError as e:
if e.response["Error"]["Code"] == "PolicyNotFound":
filesystem.backup_policy = "DISABLED"
try:
fs_policy = client.describe_file_system_policy(
FileSystemId=filesystem.id
client = self.regional_clients[filesystem.region]
try:
filesystem.backup_policy = client.describe_backup_policy(
FileSystemId=filesystem.id
)["BackupPolicy"]["Status"]
except ClientError as error:
if error.response["Error"]["Code"] == "PolicyNotFound":
filesystem.backup_policy = "DISABLED"
logger.warning(
f"{client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
else:
logger.error(
f"{client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
try:
fs_policy = client.describe_file_system_policy(
FileSystemId=filesystem.id
)
if "Policy" in fs_policy:
filesystem.policy = json.loads(fs_policy["Policy"])
except ClientError as error:
if error.response["Error"]["Code"] == "PolicyNotFound":
filesystem.policy = {}
logger.warning(
f"{client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
else:
logger.error(
f"{client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _describe_mount_targets(self, filesystem):
logger.info("EFS - Describing mount targets...")
try:
client = self.regional_clients[filesystem.region]
describe_mount_target_paginator = client.get_paginator(
"describe_mount_targets"
)
for page in describe_mount_target_paginator.paginate(
FileSystemId=filesystem.id
):
for mount_target in page["MountTargets"]:
mount_target_id = mount_target["MountTargetId"]
mount_target_arn = f"arn:{self.audited_partition}:elasticfilesystem:{client.region}:{self.audited_account}:mount-target/{mount_target_id}"
if not self.audit_resources or (
is_resource_filtered(mount_target_arn, self.audit_resources)
):
self.filesystems[filesystem.arn].mount_targets.append(
MountTarget(
id=mount_target_id,
file_system_id=mount_target["FileSystemId"],
subnet_id=mount_target["SubnetId"],
)
if "Policy" in fs_policy:
filesystem.policy = json.loads(fs_policy["Policy"])
except ClientError as e:
if e.response["Error"]["Code"] == "PolicyNotFound":
filesystem.policy = {}
)
except Exception as error:
logger.error(
f"{client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _describe_access_points(self, filesystem):
logger.info("EFS - Describing access points...")
try:
client = self.regional_clients[filesystem.region]
describe_access_point_paginator = client.get_paginator(
"describe_access_points"
)
for page in describe_access_point_paginator.paginate(
FileSystemId=filesystem.id
):
for access_point in page["AccessPoints"]:
access_point_id = access_point["AccessPointId"]
access_point_arn = access_point["AccessPointArn"]
if not self.audit_resources or (
is_resource_filtered(access_point_arn, self.audit_resources)
):
self.filesystems[filesystem.arn].access_points.append(
AccessPoint(
id=access_point_id,
file_system_id=access_point["FileSystemId"],
root_directory_path=access_point["RootDirectory"][
"Path"
],
posix_user=access_point.get("PosixUser", {}),
)
)
except Exception as error:
logger.error(
f"{client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class MountTarget(BaseModel):
id: str
file_system_id: str
subnet_id: str
class AccessPoint(BaseModel):
id: str
file_system_id: str
root_directory_path: str
posix_user: dict = {}
class FileSystem(BaseModel):
id: str
arn: str
region: str
policy: Optional[dict]
backup_policy: Optional[str]
policy: Optional[dict] = {}
backup_policy: Optional[str] = "DISABLED"
encrypted: bool
mount_targets: list[MountTarget] = []
access_points: list[AccessPoint] = []
tags: Optional[list] = []
@@ -0,0 +1,6 @@
from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import (
ElasticBeanstalk,
)
from prowler.providers.common.provider import Provider
elasticbeanstalk_client = ElasticBeanstalk(Provider.get_global_provider())
@@ -0,0 +1,106 @@
from typing import Optional
from pydantic import BaseModel
from prowler.lib.logger import logger
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
from prowler.providers.aws.lib.service.service import AWSService
class ElasticBeanstalk(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.environments = {}
self.__threading_call__(self._describe_environments)
self.__threading_call__(
self._describe_configuration_settings, self.environments.values()
)
self.__threading_call__(
self._list_tags_for_resource, self.environments.values()
)
def _describe_environments(self, regional_client):
logger.info("ElasticBeanstalk - Describing environments...")
try:
describe_environment_paginator = regional_client.get_paginator(
"describe_environments"
)
for page in describe_environment_paginator.paginate():
for environment in page["Environments"]:
environment_arn = environment["EnvironmentArn"]
if not self.audit_resources or (
is_resource_filtered(environment_arn, self.audit_resources)
):
self.environments[environment_arn] = Environment(
id=environment["EnvironmentId"],
arn=environment_arn,
application_name=environment["ApplicationName"],
name=environment["EnvironmentName"],
region=regional_client.region,
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _describe_configuration_settings(self, environment):
logger.info("ElasticBeanstalk - Describing configuration settings...")
try:
regional_client = self.regional_clients[environment.region]
configuration_settings = regional_client.describe_configuration_settings(
ApplicationName=environment.application_name,
EnvironmentName=environment.name,
)
option_settings = configuration_settings["ConfigurationSettings"][0].get(
"OptionSettings", {}
)
for option in option_settings:
if (
option["Namespace"] == "aws:elasticbeanstalk:healthreporting:system"
and option["OptionName"] == "SystemType"
):
environment.health_reporting = option.get("Value", "basic")
elif (
option["Namespace"] == "aws:elasticbeanstalk:managedactions"
and option["OptionName"] == "ManagedActionsEnabled"
):
environment.managed_platform_updates = option.get("Value", "false")
elif (
option["Namespace"] == "aws:elasticbeanstalk:cloudwatch:logs"
and option["OptionName"] == "StreamLogs"
):
environment.cloudwatch_stream_logs = option.get("Value", "false")
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _list_tags_for_resource(self, resource: any):
logger.info("ElasticBeanstalk - List Tags...")
try:
regional_client = self.regional_clients[resource.region]
response = regional_client.list_tags_for_resource(ResourceArn=resource.arn)[
"ResourceTags"
]
resource.tags = response
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class Environment(BaseModel):
id: str
name: str
arn: str
region: str
application_name: str
health_reporting: Optional[str]
managed_platform_updates: Optional[str]
cloudwatch_stream_logs: Optional[str]
tags: Optional[list] = []
@@ -18,7 +18,7 @@ class elbv2_waf_acl_attached(Check):
report.status_extended = (
f"ELBv2 ALB {lb.name} is not protected by WAF Web ACL."
)
for acl in wafv2_client.web_acls:
for acl in wafv2_client.web_acls.values():
if lb_arn in acl.albs:
report.status = "PASS"
report.status_extended = f"ELBv2 ALB {lb.name} is protected by WAFv2 Web ACL {acl.name}."
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "glue_ml_transform_encrypted_at_rest",
"CheckTitle": "Check if Glue ML Transform Encryption at Rest is Enabled",
"CheckType": [],
"ServiceName": "glue",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:glue:region:account-id:mlTransform/transform-id",
"Severity": "medium",
"ResourceType": "Other",
"Description": "This control checks whether an AWS Glue machine learning transform is encrypted at rest. The control fails if the machine learning transform isn't encrypted at rest.",
"Risk": "Data at rest refers to data that's stored in persistent, non-volatile storage for any duration. Encrypting data at rest helps you protect its confidentiality, which reduces the risk that an unauthorized user can access it.",
"RelatedUrl": "https://docs.aws.amazon.com/glue/latest/dg/encryption-at-rest.html",
"Remediation": {
"Code": {
"CLI": "aws glue update-ml-transform --transform-id <transform-id> --encryption-at-rest {\"Enabled\":true,\"KmsKey\":\"<kms-key-arn>\"}",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/glue-controls.html#glue-3",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable encryption at rest for Glue ML Transforms using AWS KMS keys.",
"Url": "https://docs.aws.amazon.com/glue/latest/dg/encryption-at-rest.html"
}
},
"Categories": [
"encryption"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,28 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.glue.glue_client import glue_client
class glue_ml_transform_encrypted_at_rest(Check):
def execute(self):
findings = []
for ml_transform_arn, ml_transform in glue_client.ml_transforms.items():
report = Check_Report_AWS(self.metadata())
report.resource_id = ml_transform.id
report.resource_arn = ml_transform_arn
report.region = ml_transform.region
report.resource_tags = ml_transform.tags
report.status = "PASS"
report.status_extended = (
f"Glue ML Transform {ml_transform.name} is encrypted at rest."
)
if ml_transform.user_data_encryption == "DISABLED":
report.status = "FAIL"
report.status_extended = (
f"Glue ML Transform {ml_transform.name} is not encrypted at rest."
)
findings.append(report)
return findings
@@ -27,6 +27,9 @@ class Glue(AWSService):
self.jobs = []
self.__threading_call__(self._get_jobs)
self.__threading_call__(self._list_tags, self.jobs)
self.ml_transforms = {}
self.__threading_call__(self._get_ml_transforms)
self.__threading_call__(self._list_tags, self.ml_transforms.values())
def _get_data_catalog_arn_template(self, region):
return f"arn:{self.audited_partition}:glue:{region}:{self.audited_account}:data-catalog"
@@ -219,6 +222,30 @@ class Glue(AWSService):
f"{resource.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _get_ml_transforms(self, regional_client):
logger.info("Glue - Getting ML Transforms...")
try:
transforms = regional_client.get_ml_transforms()["Transforms"]
for transform in transforms:
ml_transform_arn = f"arn:{self.audited_partition}:glue:{regional_client.region}:{self.audited_account}:mlTransform/{transform['TransformId']}"
if not self.audit_resources or is_resource_filtered(
ml_transform_arn, self.audit_resources
):
self.ml_transforms[ml_transform_arn] = MLTransform(
arn=ml_transform_arn,
id=transform["TransformId"],
name=transform["Name"],
user_data_encryption=transform.get("TransformEncryption", {})
.get("MlUserDataEncryption", {})
.get("MlUserDataEncryptionMode", "DISABLED"),
region=regional_client.region,
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class Connection(BaseModel):
name: str
@@ -272,3 +299,12 @@ class SecurityConfig(BaseModel):
jb_encryption: str
jb_key_arn: Optional[str]
region: str
class MLTransform(BaseModel):
arn: str
id: str
name: str
user_data_encryption: str
region: str
tags: Optional[list]
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "guardduty_ec2_malware_protection_enabled",
"CheckTitle": "Ensure that GuardDuty Malware Protection for EC2 is enabled.",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "guardduty",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:guardduty:region:account-id/detector-id",
"Severity": "high",
"ResourceType": "AwsGuardDutyDetector",
"Description": "GuardDuty Malware Protection for EC2 helps you detect the potential presence of malware by scanning the Amazon Elastic Block Store (Amazon EBS) volumes that are attached to Amazon Elastic Compute Cloud (Amazon EC2) instances and container workloads.",
"Risk": "Malware can compromise your EC2 instances and container workloads, leading to data breaches, data exfiltration, and other security incidents.",
"RelatedUrl": "https://docs.aws.amazon.com/guardduty/latest/ug/malware-protection.html",
"Remediation": {
"Code": {
"CLI": "aws guardduty update-detector --detector-id <detector-id> --data-sources MalwareProtection={ScanEc2InstanceWithFindings={EbsVolumes=true}}",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/guardduty-controls.html#guardduty-8",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable Malware Protection for EC2 in GuardDuty.",
"Url": "https://docs.aws.amazon.com/guardduty/latest/ug/configure-malware-protection-single-account.html"
}
},
"Categories": [],
"Notes": "",
"DependsOn": [],
"RelatedTo": []
}
@@ -0,0 +1,21 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.guardduty.guardduty_client import guardduty_client
class guardduty_ec2_malware_protection_enabled(Check):
def execute(self):
findings = []
for detector in guardduty_client.detectors:
if detector.status:
report = Check_Report_AWS(self.metadata())
report.region = detector.region
report.resource_id = detector.id
report.resource_arn = detector.arn
report.resource_tags = detector.tags
report.status = "FAIL"
report.status_extended = f"GuardDuty detector {detector.id} does not have Malware Protection for EC2 enabled."
if detector.ec2_malware_protection:
report.status = "PASS"
report.status_extended = f"GuardDuty detector {detector.id} has Malware Protection for EC2 enabled."
findings.append(report)
return findings
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "guardduty_eks_audit_log_enabled",
"CheckTitle": "GuardDuty EKS Audit Log Monitoring Enabled",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices/Runtime Behavior Analysis"
],
"ServiceName": "guardduty",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:guardduty:region:account-id/detector-id",
"Severity": "high",
"ResourceType": "AwsGuardDutyDetector",
"Description": "Checks whether GuardDuty EKS Audit Log Monitoring is enabled as source in a detector.",
"Risk": "Without GuardDuty EKS Audit Log Monitoring enabled, you may not be able to detect potentially suspicious activities in your Amazon Elastic Kubernetes Service (Amazon EKS) clusters.",
"RelatedUrl": "https://docs.aws.amazon.com/guardduty/latest/ug/kubernetes-protection.html",
"Remediation": {
"Code": {
"CLI": "aws guardduty update-detector --detector-id <detector-id> --data-sources Kubernetes={AuditLogs={Enable=true}}",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/guardduty-controls.html#guardduty-5",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable GuardDuty EKS Audit Log Monitoring to detect potentially suspicious activities in your Amazon Elastic Kubernetes Service (Amazon EKS) clusters.",
"Url": "https://docs.aws.amazon.com/guardduty/latest/ug/eks-protection-enable-standalone-account.html"
}
},
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,21 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.guardduty.guardduty_client import guardduty_client
class guardduty_eks_audit_log_enabled(Check):
def execute(self):
findings = []
for detector in guardduty_client.detectors:
if detector.status:
report = Check_Report_AWS(self.metadata())
report.region = detector.region
report.resource_id = detector.id
report.resource_arn = detector.arn
report.resource_tags = detector.tags
report.status = "FAIL"
report.status_extended = f"GuardDuty detector {detector.id} does not have EKS Audit Log Monitoring enabled."
if detector.eks_audit_log_protection:
report.status = "PASS"
report.status_extended = f"GuardDuty detector {detector.id} has EKS Audit Log Monitoring enabled."
findings.append(report)
return findings
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "guardduty_lambda_protection_enabled",
"CheckTitle": "Check if GuardDuty Lambda Protection is enabled.",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "guardduty",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:guardduty:region:account-id/detector-id",
"Severity": "high",
"ResourceType": "",
"Description": "GuardDuty Lambda Protection helps you identify potential security threats when an AWS Lambda function gets invoked. After you enable Lambda Protection, GuardDuty starts monitoring Lambda network activity logs associated with the Lambda functions in your AWS account.",
"Risk": "If Lambda Protection is not enabled, GuardDuty will not be able to monitor Lambda network activity logs and may miss potential security threats.",
"RelatedUrl": "https://docs.aws.amazon.com/guardduty/latest/ug/lambda-protection.html",
"Remediation": {
"Code": {
"CLI": "aws guardduty update-detector --detector-id <detector-id> --features Name=LAMBDA_NETWORK_LOGS,Status=ENABLED",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/guardduty-controls.html#guardduty-6",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable Lambda Protection in your GuardDuty detector to start monitoring Lambda Network Activity in your account.",
"Url": "https://docs.aws.amazon.com/guardduty/latest/ug/configure-lambda-protection-standalone-acc.html"
}
},
"Categories": [],
"Notes": "",
"DependsOn": [],
"RelatedTo": []
}
@@ -0,0 +1,21 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.guardduty.guardduty_client import guardduty_client
class guardduty_lambda_protection_enabled(Check):
def execute(self):
findings = []
for detector in guardduty_client.detectors:
if detector.status:
report = Check_Report_AWS(self.metadata())
report.region = detector.region
report.resource_id = detector.id
report.resource_arn = detector.arn
report.resource_tags = detector.tags
report.status = "FAIL"
report.status_extended = f"GuardDuty detector {detector.id} does not have Lambda Protection enabled."
if detector.lambda_protection:
report.status = "PASS"
report.status_extended = f"GuardDuty detector {detector.id} has Lambda Protection enabled."
findings.append(report)
return findings
@@ -15,7 +15,7 @@
"RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/guard-duty-rds-protection.html",
"Remediation": {
"Code": {
"CLI": "",
"CLI": "aws guardduty update-detector --detector-id <detector-id> --features Name=RDS_LOGIN_EVENTS,Status=ENABLED",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/guardduty-controls.html#guardduty-9",
"Terraform": ""
@@ -15,7 +15,7 @@
"RelatedUrl": "https://docs.aws.amazon.com/guardduty/latest/ug/s3_detection.html",
"Remediation": {
"Code": {
"CLI": "aws guardduty update-detector --detector-id <detector-id> --data-sources '{\"S3Logs\": {\"Enable\": true}}'",
"CLI": "aws guardduty update-detector --detector-id <detector-id> --data-sources S3Logs={Enable=true}}'",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/guardduty-controls.html#guardduty-10",
"Terraform": ""
@@ -13,7 +13,7 @@ class GuardDuty(AWSService):
super().__init__(__class__.__name__, provider)
self.detectors = []
self.__threading_call__(self._list_detectors)
self._get_detector()
self.__threading_call__(self._get_detector, self.detectors)
self._list_findings()
self._list_members()
self._get_administrator_account()
@@ -53,38 +53,53 @@ class GuardDuty(AWSService):
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _get_detector(self):
def _get_detector(self, detector):
logger.info("GuardDuty - getting detector info...")
try:
for detector in self.detectors:
try:
if detector.id and detector.enabled_in_account:
regional_client = self.regional_clients[detector.region]
detector_info = regional_client.get_detector(
DetectorId=detector.id
)
if (
"Status" in detector_info
and detector_info["Status"] == "ENABLED"
):
detector.status = True
if detector.id and detector.enabled_in_account:
detector_info = self.regional_clients[detector.region].get_detector(
DetectorId=detector.id
)
if detector_info.get("Status", "DISABLED") == "ENABLED":
detector.status = True
data_sources = detector_info.get("DataSources", {})
s3_logs = data_sources.get("S3Logs", {})
if s3_logs.get("Status") == "ENABLED":
detector.s3_protection = True
data_sources = detector_info.get("DataSources", {})
for feat in detector_info.get("Features", []):
if (
feat.get("Name") == "RDS_LOGIN_EVENTS"
and feat.get("Status", "DISABLED") == "ENABLED"
):
detector.rds_protection = True
s3_logs = data_sources.get("S3Logs", {})
if s3_logs.get("Status", "DISABLED") == "ENABLED":
detector.s3_protection = True
detector.eks_audit_log_protection = (
True
if data_sources.get("Kubernetes", {})
.get("AuditLogs", {})
.get("Status", "DISABLED")
== "ENABLED"
else False
)
detector.ec2_malware_protection = (
True
if data_sources.get("MalwareProtection", {})
.get("ScanEc2InstanceWithFindings", {})
.get("EbsVolumes", {})
.get("Status", "DISABLED")
== "ENABLED"
else False
)
for feat in detector_info.get("Features", []):
if (
feat.get("Name", "") == "RDS_LOGIN_EVENTS"
and feat.get("Status", "DISABLED") == "ENABLED"
):
detector.rds_protection = True
elif (
feat.get("Name", "") == "LAMBDA_NETWORK_LOGS"
and feat.get("Status", "DISABLED") == "ENABLED"
):
detector.lambda_protection = True
except Exception as error:
logger.error(
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
)
except Exception as error:
logger.error(
f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}"
@@ -207,3 +222,6 @@ class Detector(BaseModel):
tags: Optional[list] = []
s3_protection: bool = False
rds_protection: bool = False
eks_audit_log_protection: bool = False
lambda_protection: bool = False
ec2_malware_protection: bool = False
@@ -5,17 +5,23 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
class iam_check_saml_providers_sts(Check):
def execute(self) -> Check_Report_AWS:
findings = []
if iam_client.saml_providers:
for provider in iam_client.saml_providers:
report = Check_Report_AWS(self.metadata())
provider_name = provider["Arn"].split("/")[1]
report.resource_id = provider_name
report.resource_arn = provider["Arn"]
report.region = iam_client.region
report.status = "PASS"
report.status_extended = (
f"SAML Provider {provider_name} has been found."
)
findings.append(report)
if not iam_client.saml_providers and iam_client.saml_providers is not None:
report = Check_Report_AWS(self.metadata())
report.resource_id = iam_client.audited_account
report.resource_arn = iam_client.audited_account_arn
report.region = iam_client.region
report.status = "FAIL"
report.status_extended = "No SAML Providers found."
findings.append(report)
for provider_arn, provider in iam_client.saml_providers.items():
report = Check_Report_AWS(self.metadata())
report.resource_id = provider.name
report.resource_arn = provider_arn
report.resource_tags = provider.tags
report.region = iam_client.region
report.status = "PASS"
report.status_extended = f"SAML Provider {provider.name} has been found."
findings.append(report)
return findings
@@ -13,6 +13,7 @@ class iam_no_expired_server_certificates_stored(Check):
report.region = iam_client.region
report.resource_id = certificate.id
report.resource_arn = certificate.arn
report.resource_tags = certificate.tags
expiration_days = (datetime.now(timezone.utc) - certificate.expiration).days
if expiration_days >= 0:
report.status = "FAIL"
@@ -16,6 +16,7 @@ class iam_policy_attached_only_to_group_or_roles(Check):
report.status_extended = f"User {user.name} has the policy {policy['PolicyName']} attached."
report.resource_id = f"{user.name}/{policy['PolicyName']}"
report.resource_arn = user.arn
report.resource_tags = user.tags
findings.append(report)
if user.inline_policies:
for policy in user.inline_policies:
@@ -25,6 +26,7 @@ class iam_policy_attached_only_to_group_or_roles(Check):
report.status_extended = f"User {user.name} has the inline policy {policy} attached."
report.resource_id = f"{user.name}/{policy}"
report.resource_arn = user.arn
report.resource_tags = user.tags
findings.append(report)
else:
@@ -32,6 +34,7 @@ class iam_policy_attached_only_to_group_or_roles(Check):
report.region = iam_client.region
report.resource_id = user.name
report.resource_arn = user.arn
report.resource_tags = user.tags
report.status = "PASS"
report.status_extended = (
f"User {user.name} has no inline or attached policies."
@@ -12,9 +12,15 @@ maximum_expiration_days = 90
class iam_rotate_access_key_90_days(Check):
def execute(self) -> Check_Report_AWS:
findings = []
response = iam_client.credential_report
for user in response:
for user in iam_client.credential_report:
# Search user in iam_client.users to get tags
user_tags = []
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
user_tags = iam_user.tags
break
if (
user["access_key_1_last_rotated"] == "N/A"
and user["access_key_2_last_rotated"] == "N/A"
@@ -23,6 +29,7 @@ class iam_rotate_access_key_90_days(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "PASS"
report.status_extended = (
f"User {user['user']} does not have access keys."
@@ -44,6 +51,7 @@ class iam_rotate_access_key_90_days(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not rotated access key 1 in over 90 days ({access_key_1_last_rotated.days} days)."
findings.append(report)
@@ -60,6 +68,7 @@ class iam_rotate_access_key_90_days(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not rotated access key 2 in over 90 days ({access_key_2_last_rotated.days} days)."
findings.append(report)
@@ -69,6 +78,7 @@ class iam_rotate_access_key_90_days(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "PASS"
report.status_extended = f"User {user['user']} does not have access keys older than 90 days."
findings.append(report)
@@ -47,7 +47,6 @@ def is_service_role(role):
return False
################## IAM
class IAM(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
@@ -69,9 +68,7 @@ class IAM(AWSService):
self._list_attached_role_policies()
self._list_mfa_devices()
self.password_policy = self._get_password_policy()
support_policy_arn = (
f"arn:{self.audited_partition}:iam::aws:policy/AWSSupportAccess"
)
support_policy_arn = f"arn:{self.audited_partition}:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
self.entities_role_attached_to_support_policy = (
self._list_entities_role_for_policy(support_policy_arn)
)
@@ -91,13 +88,21 @@ class IAM(AWSService):
self._list_inline_role_policies()
self.saml_providers = self._list_saml_providers()
self.server_certificates = self._list_server_certificates()
self._list_tags_for_resource()
self.access_keys_metadata = {}
self._get_access_keys_metadata()
self.last_accessed_services = {}
self._get_last_accessed_services()
self.user_temporary_credentials_usage = {}
self._get_user_temporary_credentials_usage()
# List missing tags
self.__threading_call__(self._list_tags, self.users)
self.__threading_call__(self._list_tags, self.roles)
self.__threading_call__(
self._list_tags,
[policy for policy in self.policies if policy.type == "Custom"],
)
self.__threading_call__(self._list_tags, self.server_certificates)
self.__threading_call__(self._list_tags, self.saml_providers.values())
def _get_client(self):
return self.client
@@ -735,17 +740,31 @@ class IAM(AWSService):
def _list_saml_providers(self):
logger.info("IAM - List SAML Providers...")
saml_providers = {}
try:
saml_providers = self.client.list_saml_providers()["SAMLProviderList"]
saml_providers_list = self.client.list_saml_providers()["SAMLProviderList"]
for provider in saml_providers_list:
if not self.audit_resources or (
is_resource_filtered(provider["Arn"], self.audit_resources)
):
saml_providers[provider["Arn"]] = SAMLProvider(
name=provider["Arn"].split("/")[-1], arn=provider["Arn"]
)
except ClientError as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
if error.response["Error"]["Code"] == "AccessDenied":
saml_providers = None
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
saml_providers = None
finally:
return saml_providers
def _list_server_certificates(self):
return saml_providers
def _list_server_certificates(self) -> list:
logger.info("IAM - List Server Certificates...")
try:
server_certificates = []
@@ -770,71 +789,30 @@ class IAM(AWSService):
finally:
return server_certificates
def _list_tags_for_resource(self):
def _list_tags(self, resource: any):
logger.info("IAM - List Tags...")
try:
if self.roles:
for role in self.roles:
try:
response = self.client.list_role_tags(RoleName=role.name)[
"Tags"
]
role.tags = response
except ClientError as error:
if error.response["Error"]["Code"] == "NoSuchEntity":
role.tags = []
else:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
try:
for user in self.users:
try:
response = self.client.list_user_tags(UserName=user.name)["Tags"]
user.tags = response
except ClientError as error:
if error.response["Error"]["Code"] == "NoSuchEntity":
user.tags = []
else:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
try:
for policy in self.policies:
try:
if policy.type != "Inline":
response = self.client.list_policy_tags(PolicyArn=policy.arn)[
"Tags"
]
policy.tags = response
except ClientError as error:
if error.response["Error"]["Code"] == "NoSuchEntity":
policy.tags = []
else:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
if isinstance(resource, Role):
resource.tags = self.client.list_role_tags(RoleName=resource.name).get(
"Tags", []
)
elif isinstance(resource, User):
resource.tags = self.client.list_user_tags(UserName=resource.name).get(
"Tags", []
)
elif isinstance(resource, Policy):
if resource.type == "Custom":
resource.tags = self.client.list_policy_tags(
PolicyArn=resource.arn
).get("Tags", [])
elif isinstance(resource, Certificate):
resource.tags = self.client.list_server_certificate_tags(
ServerCertificateName=resource.name
).get("Tags", [])
elif isinstance(resource, SAMLProvider):
resource.tags = self.client.list_saml_provider_tags(
SAMLProviderArn=resource.arn
).get("Tags", [])
except Exception as error:
logger.error(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -954,7 +932,7 @@ class User(BaseModel):
console_access: Optional[bool]
attached_policies: list[dict] = []
inline_policies: list[str] = []
tags: Optional[list] = []
tags: Optional[list]
class Role(BaseModel):
@@ -964,7 +942,7 @@ class Role(BaseModel):
is_service_role: bool
attached_policies: list[dict] = []
inline_policies: list[str] = []
tags: Optional[list] = []
tags: Optional[list]
class Group(BaseModel):
@@ -993,6 +971,7 @@ class Certificate(BaseModel):
id: str
arn: str
expiration: datetime
tags: Optional[list]
class Policy(BaseModel):
@@ -1004,3 +983,9 @@ class Policy(BaseModel):
attached: bool
document: Optional[dict]
tags: Optional[list] = []
class SAMLProvider(BaseModel):
name: str
arn: str
tags: Optional[list]
@@ -8,17 +8,13 @@ class iam_support_role_created(Check):
if iam_client.entities_role_attached_to_support_policy is not None:
report = Check_Report_AWS(self.metadata())
report.region = iam_client.region
report.resource_id = iam_client.audited_account
report.resource_arn = (
f"arn:{iam_client.audited_partition}:iam::aws:policy/AWSSupportAccess"
)
report.resource_id = "AWSSupportServiceRolePolicy"
report.resource_arn = f"arn:{iam_client.audited_partition}:iam::aws:policy/aws-service-role/AWSSupportServiceRolePolicy"
if iam_client.entities_role_attached_to_support_policy:
report.status = "PASS"
report.status_extended = f"AWS Support Access policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}."
report.status_extended = f"Support policy attached to role {iam_client.entities_role_attached_to_support_policy[0]['RoleName']}."
else:
report.status = "FAIL"
report.status_extended = (
"AWS Support Access policy is not attached to any role."
)
report.status_extended = "Support policy is not attached to any role."
findings.append(report)
return findings
@@ -14,6 +14,13 @@ class iam_user_accesskey_unused(Check):
)
findings = []
for user in iam_client.credential_report:
# Search user in iam_client.users to get tags
user_tags = []
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
user_tags = iam_user.tags
break
if (
user["access_key_1_active"] != "true"
and user["access_key_2_active"] != "true"
@@ -22,6 +29,7 @@ class iam_user_accesskey_unused(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "PASS"
report.status_extended = (
f"User {user['user']} does not have access keys."
@@ -41,6 +49,7 @@ class iam_user_accesskey_unused(Check):
report.region = iam_client.region
report.resource_id = user["user"] + "/AccessKey1"
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not used access key 1 in the last {maximum_expiration_days} days ({access_key_1_last_used_date.days} days)."
findings.append(report)
@@ -56,6 +65,7 @@ class iam_user_accesskey_unused(Check):
report.region = iam_client.region
report.resource_id = user["user"] + "/AccessKey2"
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "FAIL"
report.status_extended = f"User {user['user']} has not used access key 2 in the last {maximum_expiration_days} days ({access_key_2_last_used_date.days} days)."
findings.append(report)
@@ -65,6 +75,7 @@ class iam_user_accesskey_unused(Check):
report.region = iam_client.region
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.resource_tags = user_tags
report.status = "PASS"
report.status_extended = f"User {user['user']} does not have unused access keys for {maximum_expiration_days} days."
findings.append(report)
@@ -13,6 +13,11 @@ class iam_user_mfa_enabled_console_access(Check):
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.region = iam_client.region
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
report.resource_tags = iam_user.tags
break
# check if the user has password enabled
if user["password_enabled"] == "true":
if user["mfa_active"] == "false":
@@ -54,4 +54,9 @@ class iam_user_no_setup_initial_access_key(Check):
report.resource_arn = user["arn"]
report.status = status
report.status_extended = status_extended
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
report.resource_tags = iam_user.tags
break
findings.append(report)
@@ -13,6 +13,11 @@ class iam_user_two_active_access_key(Check):
report.resource_id = user["user"]
report.resource_arn = user["arn"]
report.region = iam_client.region
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user["arn"]:
report.resource_tags = iam_user.tags
break
if (
user["access_key_1_active"] == "true"
and user["access_key_2_active"] == "true"
@@ -17,6 +17,11 @@ class iam_user_with_temporary_credentials(Check):
report.resource_id = user_name
report.resource_arn = user_arn
report.region = iam_client.region
# Search user in iam_client.users to get tags
for iam_user in iam_client.users:
if iam_user.arn == user_arn:
report.resource_tags = iam_user.tags
break
report.status = "PASS"
report.status_extended = f"User {user_name} doesn't have long lived credentials with access to other services than IAM or STS."
@@ -0,0 +1,4 @@
from prowler.providers.aws.services.kinesis.kinesis_service import Kinesis
from prowler.providers.common.provider import Provider
kinesis_client = Kinesis(Provider.get_global_provider())
@@ -0,0 +1,95 @@
from enum import Enum
from typing import Optional
from pydantic import BaseModel
from prowler.lib.logger import logger
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
from prowler.providers.aws.lib.service.service import AWSService
class Kinesis(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.streams = {}
self.__threading_call__(self._list_streams)
self.__threading_call__(self._describe_stream, self.streams.values())
self.__threading_call__(self._list_tags_for_stream, self.streams.values())
def _list_streams(self, regional_client):
logger.info("Kinesis - Listing Kinesis Streams...")
try:
list_streams_paginator = regional_client.get_paginator("list_streams")
for page in list_streams_paginator.paginate():
for stream in page["StreamSummaries"]:
arn = stream["StreamARN"]
if not self.audit_resources or (
is_resource_filtered(arn, self.audit_resources)
):
self.streams[arn] = Stream(
arn=arn,
name=stream["StreamName"],
region=regional_client.region,
status=StreamStatus(stream.get("StreamStatus", "ACTIVE")),
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _describe_stream(self, stream):
logger.info(f"Kinesis - Describing Stream {stream.name}...")
try:
stream_description = (
self.regional_clients[stream.region]
.describe_stream(StreamName=stream.name)
.get("StreamDescription", {})
)
stream.encrypted_at_rest = EncryptionType(
stream_description.get("EncryptionType", "NONE")
)
except Exception as error:
logger.error(
f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _list_tags_for_stream(self, stream):
logger.info(f"Kinesis - Listing tags for Stream {stream.name}...")
try:
stream.tags = (
self.regional_clients[stream.region]
.list_tags_for_stream(StreamName=stream.name)
.get("Tags", [])
)
except Exception as error:
logger.error(
f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class EncryptionType(Enum):
"""Enum for Kinesis Stream Encryption Type"""
NONE = "NONE"
KMS = "KMS"
class StreamStatus(Enum):
"""Enum for Kinesis Stream Status"""
ACTIVE = "ACTIVE"
CREATING = "CREATING"
DELETING = "DELETING"
UPDATING = "UPDATING"
class Stream(BaseModel):
"""Model for Kinesis Stream"""
arn: str
region: str
name: str
status: StreamStatus
tags: Optional[list]
encrypted_at_rest: EncryptionType = EncryptionType.NONE

Some files were not shown because too many files have changed in this diff Show More