Compare commits

..
Author SHA1 Message Date
pedrooot 4a3c9b8dc3 chore(v4): show prowler cloud + version upgrade 2026-08-13 13:02:57 +02:00
Prowler BotandPedro Martín 4fb010847f fix(compliance_tables): add correct values for findings (#6126)
Co-authored-by: Pedro Martín <pedromarting3@gmail.com>
2024-12-10 16:47:16 -04:00
Prowler BotandHugo Pereira Brito 8eac17d826 fix(aws): get firewall manager managed rule groups (#6123)
Co-authored-by: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com>
2024-12-10 16:47:01 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8a1abb60c9 chore(deps-dev): bump pytest from 8.3.3 to 8.3.4 (#6075)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 14:38:57 -04:00
Prowler BotandHugo Pereira Brito 74776bed06 fix(aws): check AWS Owned keys in firehose_stream_encrypted_at_rest (#6120)
Co-authored-by: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com>
2024-12-10 14:30:45 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3373a8e6c8 chore(deps): bump microsoft-kiota-abstractions from 1.6.2 to 1.6.6 (#6080)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 13:48:43 -04:00
Prowler BotandSergio Garcia 7db74516b3 fix(aws): set IAM identity as resource in threat detection (#6117)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-12-10 13:37:27 -04:00
Prowler BotandSergio Garcia 3ae2f9e0bf fix(gcp): make sure default project is active (#6112)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-12-10 11:53:28 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 91a8a13dc6 chore(deps): bump msgraph-sdk from 1.12.0 to 1.14.0 (#6074)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 11:42:18 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 700ed5ecec chore(deps-dev): bump pylint from 3.3.1 to 3.3.2 (#6099)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 10:36:15 -04:00
Prowler BotandSergio Garcia 50aebcaf88 fix(aws): set same severity for EC2 IMDSv2 checks (#6103)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-12-10 09:30:34 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ad96963706 chore(deps): bump boto3 from 1.35.66 to 1.35.77 (#6107)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 09:26:53 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 95832bdb12 chore(deps): bump botocore from 1.35.76 to 1.35.77 (#6100)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 10:55:31 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d7bdc0aaef chore(deps-dev): bump bandit from 1.7.10 to 1.8.0 (#6072)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 17:39:53 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1c0c836286 chore(deps-dev): bump coverage from 7.6.7 to 7.6.9 (#6076)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 16:51:09 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 822252d03b chore(deps): bump slack-sdk from 3.33.4 to 3.33.5 (#6077)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 15:53:24 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a20fdb30f9 chore(deps-dev): bump mkdocs-material from 9.5.45 to 9.5.48 (#6078)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 14:44:08 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2e339f964a chore(deps): bump botocore from 1.35.66 to 1.35.76 (#6071)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 13:38:26 -04:00
Sergio Garcia bb4498a3d5 chore(container): upload v4 with correct tags (#6093) 2024-12-09 18:38:14 +01:00
dae4392efc fix(backup): modify list recovery points call (#6057)
Co-authored-by: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com>
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
2024-12-09 12:55:12 -04:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>MrCloudSec
8b3a1608c2 chore(deps-dev): bump vulture from 2.13 to 2.14 (#6069)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
2024-12-09 12:22:48 -04:00
148bdacebe chore(containers): support for v4.6 branch (#6084)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-12-09 11:23:06 -04:00
Prowler BotandSergio Garcia b60edc1f6b fix(tests): use datetime.datetime.now() in GCP kms_key_rotation_enabled (#6083)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-12-09 11:06:37 -04:00
Prowler BotandPepe Fagoaga d28462b642 chore(dependabot): Update for UI and v4 (#6087)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
2024-12-09 10:45:24 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ef9b37d5c0 chore(deps): bump trufflesecurity/trufflehog from 3.84.1 to 3.85.0 (#6067)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 09:28:22 -04:00
Sergio Garcia 764f260cea chore(version): update Prowler version (#5969) 2024-11-29 13:14:08 -04:00
Prowler BotandSergio Garcia e771218bba fix(k8s): handle Kubernetes kubeconfig content correctly (#5967)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-11-29 10:19:00 -04:00
Prowler BotandStylusFrost c627a3e9aa fix(azure): containerregistry_not_publicly_accesible is not accurate (#5966)
Co-authored-by: StylusFrost <43682773+StylusFrost@users.noreply.github.com>
2024-11-29 09:53:06 -04:00
Prowler BotandRubén De la Torre Vico 1c58644118 fix(rds): add default key value to RDS event (#5965)
Co-authored-by: Rubén De la Torre Vico <rubendltv22@gmail.com>
2024-11-29 09:52:55 -04:00
Prowler BotandSergio Garcia 398fa37a23 fix(gcp): use session credentials to check if API is active (#5936)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-11-27 16:58:40 -04:00
Prowler BotandSergio Garcia 412d948585 fix(aws): exclude threat detection checks if category not present (#5934)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-11-27 12:56:00 -04:00
Prowler BotandPepe Fagoaga 6bc0432ed5 fix(list_by_service): execute lambda if requested (#5931)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
2024-11-27 11:22:49 -04:00
Pepe Fagoaga b2ebc8410a chore(version): update Prowler v4 version (#5901) 2024-11-26 08:11:16 -04:00
Prowler BotandSergio Garcia 94b6fbda91 chore(gcp): update docstring of ADC credentials (#5879)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-11-25 13:54:40 -04:00
Prowler BotandDaniel Barranquero 3ce785a7b4 chore(aws): update fixers docstring (#5878)
Co-authored-by: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com>
2024-11-25 13:01:45 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e4ecc98aae chore(deps): bump trufflesecurity/trufflehog from 3.84.0 to 3.84.1 (#5870)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-25 09:02:32 -04:00
sansns-awsandMrCloudSec 53a4befb01 feat(aws): add MemoryDB service (#5546)
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
2024-11-22 15:13:16 -04:00
fee0bf3ea1 feat(azure): AI Search service check not publicly accesible (#5846)
Co-authored-by: Rubén De la Torre Vico <rubendltv22@gmail.com>
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
2024-11-22 13:28:44 -04:00
Daniel Barranquero 6811a22651 feat(documentdb): add new fixer documentdb_cluster_public_snapshot_fixer (#5759) 2024-11-22 11:14:24 -04:00
Daniel Barranquero fe2dd69b08 feat(neptune): add new fixer neptune_cluster_public_snapshot_fixer (#5749) 2024-11-22 11:13:18 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 26a9748700 chore(deps): bump boto3 from 1.35.65 to 1.35.66 (#5860)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-22 10:34:44 -04:00
Prowler Botandsergargar cef0a54bc3 chore(regions_update): Changes in regions for AWS services (#5863)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-22 10:34:24 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 553a51ddc2 chore(deps): bump trufflesecurity/trufflehog from 3.83.7 to 3.84.0 (#5862)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-22 10:33:58 -04:00
Gary McleanandPedro Martín 61dc09d15d Fix CIS details for new EFS Controls (#5858)
Co-authored-by: Pedro Martín <pedromarting3@gmail.com>
2024-11-22 11:55:21 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 9b91ba2b91 chore(deps): bump azure-mgmt-cosmosdb from 9.6.0 to 9.7.0 (#5834)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 14:22:28 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 18d60c98d7 chore(deps): bump botocore from 1.35.65 to 1.35.66 (#5850)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 12:40:45 -04:00
Daniel BarranqueroandSergio Garcia 00054b5cd9 feat(ec2): add new fixer ec2_ebs_public_snapshot_fixer (#5825)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-11-21 12:40:28 -04:00
Pedro Martín 24fc86cbb3 fix(severity): add enum for severity values (#5856) 2024-11-21 12:28:20 -04:00
Daniel Barranquero 861fb22257 feat(kms): add new fixer kms_cmk_not_deleted_unintentionally_fixer (#5842) 2024-11-21 11:45:55 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 7e14204be8 chore(deps): bump boto3 from 1.35.64 to 1.35.65 (#5853)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 11:44:04 -04:00
Daniel Barranquero 09ea6ba6c4 feat(rds): add new fixer rds_instance_no_public_access_fixer (#5794) 2024-11-21 11:42:03 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a83725fbed chore(deps-dev): bump mkdocs-material from 9.5.44 to 9.5.45 (#5852)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 10:34:11 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 34210cfc06 chore(deps): bump google-api-python-client from 2.153.0 to 2.154.0 (#5851)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 09:22:35 -04:00
Pedro Martín 2e20d52030 feat(compliance): add ENSRD2022 for Azure and GCP (#5746) 2024-11-21 09:36:47 +01:00
sansns-awsandMrCloudSec 9b0b61ef02 feat(sgw): add storagegateway_fault_tolerance check (#5570)
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
2024-11-20 15:10:44 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0203aec9e0 chore(deps): bump botocore from 1.35.64 to 1.35.65 (#5836)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-20 11:06:49 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 862a4ad76c chore(deps): bump slack-sdk from 3.33.3 to 3.33.4 (#5837)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-20 10:17:30 -04:00
Sergio Garcia 4b7883c464 fix(kubernetes): filter apiGroup in permission checks (#5829) 2024-11-20 15:06:06 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2bf835d3d2 chore(deps): bump boto3 from 1.35.63 to 1.35.64 (#5835)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-20 09:29:24 -04:00
Prowler Botandsergargar 09733eb298 chore(regions_update): Changes in regions for AWS services (#5839)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-20 09:29:03 -04:00
Pedro Martín 096749a455 refactor(arn): fine tune arn and resources id for unknown values (#5841) 2024-11-20 12:56:51 +01:00
Pedro MartínandSergio Garcia b06e549d81 refactor(arn): refactor arn for unknown resources (#5712)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-11-19 16:21:43 -04:00
Sergio Garcia a5f5967bb2 fix(aws): exclude member accounts in IAM Root Credentials check (#5813) 2024-11-19 15:06:12 +01:00
Sergio Garcia 7a4f5f34f7 fix(kubernetes): validate seccomp profile at pod and container levels (#5814) 2024-11-19 15:04:30 +01:00
Rubén De la Torre Vico e33b081dc6 chore(iam): add exception for public policy in EKS service (#4759) 2024-11-19 09:42:56 -04:00
Prowler Botandsergargar c8fdaa3923 chore(regions_update): Changes in regions for AWS services (#5824)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-19 09:24:49 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8a491bcf7d chore(deps): bump trufflesecurity/trufflehog from 3.83.6 to 3.83.7 (#5819)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 09:24:32 -04:00
Daniel Barranquero f5e71db5e0 fix(rds): fix typo error in rds_snapshots_public_access_fixer test (#5826) 2024-11-19 09:23:52 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 456f79d80c chore(deps): bump botocore from 1.35.63 to 1.35.64 (#5818)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 09:52:56 +01:00
Sergio Garcia 9f728833a7 chore(iam): add missing service catalog permissions (#5816) 2024-11-19 09:21:20 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f01ce849dc chore(deps): bump aiohttp from 3.10.10 to 3.10.11 (#5815)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-18 18:35:48 -04:00
Hugo Pereira Brito 572d5a1f2e fix(wafv2): only list resources for regional Web ACLs (#5811) 2024-11-18 12:09:23 -04:00
Sergio Garcia c69571abcd feat(aws): add new check iam_root_credentials_management_enabled (#5801) 2024-11-18 16:59:35 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8ddb9fbb84 chore(deps): bump boto3 from 1.35.60 to 1.35.63 (#5809)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-18 11:27:20 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 193b79c221 chore(deps-dev): bump coverage from 7.6.6 to 7.6.7 (#5795)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-18 10:36:03 -04:00
Pedro Martín a25a6148f2 docs(gcp): improve docstrings (#5716) 2024-11-18 15:19:15 +01:00
Pedro Martín 0a63e707c2 docs(kubernetes): improve docstrings for methods (#5717) 2024-11-18 15:18:57 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f53a887291 chore(deps): bump botocore from 1.35.61 to 1.35.63 (#5797)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-18 09:37:53 -04:00
Prowler Botandsergargar ca35510d74 chore(regions_update): Changes in regions for AWS services (#5802)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-18 09:37:25 -04:00
Pedro MartínandPepe Fagoaga 5d7c8d9cd2 docs(aws): improve docstrings (#5714)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
2024-11-18 12:17:05 +01:00
Pedro Martín 0d01790b22 docs(azure): improve docstrings (#5715) 2024-11-18 10:46:34 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 78b518e22b chore(deps-dev): bump coverage from 7.6.4 to 7.6.6 (#5793)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-15 10:23:35 -05:00
Pepe Fagoaga 17af724995 chore(labeler): Add compliance (#5790) 2024-11-15 10:16:03 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c9898d6d01 chore(deps): bump codecov/codecov-action from 4 to 5 (#5783)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-15 09:35:54 -05:00
Prowler Botandsergargar 259538d5e4 chore(regions_update): Changes in regions for AWS services (#5784)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-15 09:33:24 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4785feae0e chore(deps): bump botocore from 1.35.60 to 1.35.61 (#5780)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-15 09:33:05 -05:00
Pedro Martín 8be83fc632 fix(compliance): use SubscriptionID instead of name for Azure CIS (#5786) 2024-11-15 14:40:33 +01:00
Pedro Martín 005d251106 fix(iam): use get to get the key (#5785) 2024-11-15 14:37:36 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b6c8adfc64 chore(deps): bump azure-storage-blob from 12.23.1 to 12.24.0 (#5767)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-14 12:01:27 -05:00
Hugo Pereira BritoandSergio Garcia 7a711095cd feat(kinesis): add new check kinesis_stream_data_retention_period (#5547)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-11-14 12:01:01 -05:00
Daniel Barranquero b0bb348480 feat(rds): add new fixer rds_snapshots_public_access_fixer (#5773) 2024-11-14 10:40:33 -05:00
Hugo Pereira BritoandSergio Garcia c1b050b8b9 feat(firehose): add new check firehose_stream_encrypted_at_rest (#5635)
Co-authored-by: Sergio Garcia <hello@mistercloudsec.com>
2024-11-14 10:27:17 -05:00
Víctor Fernández Poyatos 28c7e803ac feat(azure): Add get_regions method for provider (#5774) 2024-11-14 15:58:12 +01:00
Prowler Botandsergargar 919acfd548 chore(regions_update): Changes in regions for AWS services (#5771)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-14 08:57:26 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1586cdae5e chore(deps): bump google-api-python-client from 2.152.0 to 2.153.0 (#5763)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-14 08:57:09 -05:00
Pepe Fagoaga cb74dae296 refactor(aws): Rename get_regions and validate partition (#5772) 2024-11-14 13:02:32 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3608aa3536 chore(deps): bump boto3 from 1.35.58 to 1.35.60 (#5770)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-14 10:01:57 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c59b08c40b chore(deps): bump botocore from 1.35.59 to 1.35.60 (#5765)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-13 19:11:52 -05:00
Rubén De la Torre VicoandMrCloudSec 73361a1cea chore(ec2): add name from image information to status_extended (#5755)
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
2024-11-13 14:30:58 -05:00
Pedro Martín 794268cec5 feat(gcp): add get regions method (#5756) 2024-11-13 19:11:42 +01:00
Pedro Martín 06b41cf8e6 refactor(azure): get locations with self session (#5751) 2024-11-13 19:00:18 +01:00
Pedro Martín a419b4b898 feat(aws): get regions by partition (#5748) 2024-11-13 18:52:50 +01:00
Rubén De la Torre Vico 622bc48688 fix(ec2): add default value to Name key for image information (#5747) 2024-11-13 15:22:52 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c0659f712a chore(deps): bump botocore from 1.35.58 to 1.35.59 (#5740)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-13 09:20:49 -05:00
Sergio Garcia 796983a530 fix(gcp): scan only ACTIVE projects (#5743) 2024-11-13 15:18:42 +01:00
Prowler Botandsergargar 8dc042e594 chore(regions_update): Changes in regions for AWS services (#5744)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-13 08:13:35 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e881a0f274 chore(deps): bump google-api-python-client from 2.151.0 to 2.152.0 (#5742)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-13 08:13:18 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5641160177 chore(deps): bump boto3 from 1.35.57 to 1.35.58 (#5741)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 18:17:31 -05:00
Hugo Pereira BritoandMrCloudSec dca49b1972 feat(aws): add new service firehose (#5620)
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
2024-11-12 17:41:46 -05:00
Rubén De la Torre VicoandSergio b8b60e6bc5 feat(prowler-check-kreator): ProwlerChecKreator first version (#5099)
Co-authored-by: Sergio <sergio@prowler.com>
2024-11-12 15:00:09 -05:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Rubén De la Torre VicoSergio GarciaSergio
9d65b49cb4 chore(deps): bump azure-mgmt-network from 27.0.0 to 28.0.0 (#5703)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rubén De la Torre Vico <ruben@prowler.com>
Co-authored-by: Sergio Garcia <38561120+MrCloudSec@users.noreply.github.com>
Co-authored-by: Sergio <sergio@prowler.com>
2024-11-12 13:37:51 -05:00
Sergio Garcia f1334190d8 fix(aws): remove cloudwatch_log_group_no_critical_pii_in_logs check (#5736) 2024-11-12 12:50:47 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c434181dfd chore(deps): bump azure-mgmt-containerservice from 32.1.0 to 33.0.0 (#5706)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 11:43:21 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f3cfacae9a chore(deps): bump msgraph-sdk from 1.11.0 to 1.12.0 (#5722)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 10:26:10 -05:00
sansns-aws 77c7986797 feat(aws): Update check metadata with logging category (#5639) 2024-11-12 09:26:52 -05:00
Prowler Botandsergargar 2ac716d6db chore(regions_update): Changes in regions for AWS services (#5732)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-12 09:25:12 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> daee5fb4d2 chore(deps): bump microsoft-kiota-abstractions from 1.6.0 to 1.6.2 (#5720)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 13:46:19 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d587d40451 chore(deps): bump botocore from 1.35.57 to 1.35.58 (#5721)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 19:32:42 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f0cd88bd0e chore(deps): bump trufflesecurity/trufflehog from 3.83.5 to 3.83.6 (#5723)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 19:32:13 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> aca17904fa chore(deps-dev): bump mkdocs-git-revision-date-localized-plugin from 1.2.9 to 1.3.0 (#5704)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 14:24:40 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0157802ac1 chore(deps-dev): bump pytest-randomly from 3.15.0 to 3.16.0 (#5705)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 12:12:44 -05:00
sansns-awsandSergio 10766d708d feat(mq): add mq_broker_not_publicly_accessible check (#5604)
Co-authored-by: Sergio <sergio@prowler.com>
2024-11-11 12:12:21 -05:00
Mario Rodriguez LopezandSergio Garcia f231d8b080 feat(appsync): add new check appsync_field_level_logging_enabled (#5602)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-11-11 10:23:13 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 590a7b2697 chore(deps): bump boto3 from 1.35.55 to 1.35.57 (#5719)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 10:04:32 -05:00
Hugo Pereira Brito 3c3421644f fix(docs): provider typo (#5713) 2024-11-11 09:21:54 -05:00
Pedro MartínandPepe Fagoaga f1f68da25d feat(jira): add jira integration (#5629)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
2024-11-11 15:00:31 +01:00
Prowler Botandsergargar 48df7fdebf chore(regions_update): Changes in regions for AWS services (#5709)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-11 08:51:17 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f2e8691bf4 chore(deps): bump botocore from 1.35.56 to 1.35.57 (#5702)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 08:50:28 -05:00
Matt Johnson 344d54155a docs: Update contact.md with new Slack join URL (#5671) 2024-11-11 12:06:16 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 8ecffa3039 chore(deps): bump trufflesecurity/trufflehog from 3.83.4 to 3.83.5 (#5708)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 10:11:40 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> efbbfc1c68 chore(deps): bump azure-mgmt-resource from 23.1.1 to 23.2.0 (#5684)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 15:18:01 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dc68c1b955 chore(deps): bump msgraph-sdk from 1.8.0 to 1.11.0 (#5687)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 14:09:14 -05:00
Mario Rodriguez Lopez 5de13bdd8a fix(ec2): unique finding per Security Group in high risk ports check (#5697) 2024-11-08 14:08:27 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 5d0f498425 chore(deps): bump botocore from 1.35.55 to 1.35.56 (#5683)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 12:41:05 -05:00
Mario Rodriguez LopezandSergio Garcia 716558ffcb feat(servicecatalog): Add new check servicecatalog_portfolio_shared_within_organization_only (#5632)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-11-08 12:22:13 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 23929b3e68 chore(deps): bump dash from 2.18.1 to 2.18.2 (#5682)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 11:50:17 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a5612abc8c chore(deps-dev): bump safety from 3.2.8 to 3.2.9 (#5681)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 10:54:18 -05:00
Prowler Botandsergargar 78dddc1e03 chore(regions_update): Changes in regions for AWS services (#5694)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-08 10:53:57 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 76020d4d47 chore(deps): bump alive-progress from 3.1.5 to 3.2.0 (#5689)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 09:54:57 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b0af1390b5 chore(deps): bump trufflesecurity/trufflehog from 3.83.3 to 3.83.4 (#5692)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 11:48:59 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> bc3cd43126 chore(deps): bump slack-sdk from 3.33.1 to 3.33.3 (#5688)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 11:19:02 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 087dae07d8 chore(deps-dev): bump coverage from 7.6.1 to 7.6.4 (#5686)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 09:04:20 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0baf4fb224 chore(deps): bump boto3 from 1.35.29 to 1.35.55 (#5685)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-08 08:15:43 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0f8ea48f2f chore(deps): bump azure-mgmt-containerservice from 32.0.0 to 32.1.0 (#5664)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 15:49:57 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> ec207c50ce chore(deps): bump microsoft-kiota-abstractions from 1.3.3 to 1.6.0 (#5662)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 14:54:13 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> b59b40b822 chore(deps): bump azure-keyvault-keys from 4.9.0 to 4.10.0 (#5660)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 13:26:51 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> aa51045329 chore(deps-dev): bump mkdocs-material from 9.5.39 to 9.5.44 (#5659)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 12:36:15 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1a9f854063 chore(deps): bump google-api-python-client from 2.147.0 to 2.151.0 (#5661)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 11:42:31 -05:00
Mario Rodriguez LopezandSergio Garcia 6bdcb509e1 feat(appsync): add new check appsync_graphql_apis_no_api_key_authentication (#5591)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-11-07 11:42:07 -05:00
Sergio Garcia ce1e9de104 chore(aws): deprecate glue_etl_jobs_logging_enabled check (#5670) 2024-11-07 10:25:32 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 2471bc569a chore(deps): bump botocore from 1.35.29 to 1.35.55 (#5663)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 10:22:14 -05:00
Daniel BarranqueroandSergio d0ef75d8d9 feat(dms): add new check dms_replication_task_target_logging_enabled (#5631)
Co-authored-by: Sergio <sergio@prowler.com>
2024-11-07 10:19:44 -05:00
Sergio Garcia aa79a289ce fix(aws): update EKS check in compliance frameworks (#5672) 2024-11-07 15:56:55 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 0340ab9570 chore(deps-dev): bump pytest-cov from 5.0.0 to 6.0.0 (#5666)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 09:17:18 -05:00
thomscodeandPepe Fagoaga a2929f2efb fix(mutelist): set arguments while loading providers (#5653)
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
2024-11-07 09:12:29 -05:00
Prowler Botandsergargar bf4db86dec chore(regions_update): Changes in regions for AWS services (#5655)
Co-authored-by: sergargar <38561120+sergargar@users.noreply.github.com>
2024-11-07 08:22:22 -05:00
Daniel Barranquero a339dafcc6 fix(guardduty): fix guardduty_is_enabled_fixer test (#5668) 2024-11-07 08:21:49 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> f376516aad chore(deps-dev): bump vulture from 2.12 to 2.13 (#5665)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 08:20:54 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 816b49fac5 chore(deps-dev): bump black from 24.8.0 to 24.10.0 (#5667)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 12:55:16 +01:00
Pepe Fagoaga 6851350093 fix(lock): Use detect-secrets from package not repo (#5656) 2024-11-07 11:30:46 +01:00
Daniel BarranqueroandSergio d5873c0437 feat(dms): add new check dms_replication_task_source_logging_enabled (#5627)
Co-authored-by: Sergio <sergio@prowler.com>
2024-11-06 15:50:48 -05:00
Mario Rodriguez LopezandSergio a2dba30869 feat(servicecatalog): Add new service servicecatalog (#5618)
Co-authored-by: Sergio <sergio@prowler.com>
2024-11-06 12:02:14 -05:00
Mario Rodriguez LopezandSergio 0662dff13f feat(appsync): Add new service AppSync (#5589)
Co-authored-by: Sergio <sergio@prowler.com>
2024-11-06 11:50:27 -05:00
Daniel BarranqueroandSergio Garcia 0ae26bddfc feat(dms): add new check dms_endpoint_redis_tls_enabled (#5583)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
2024-11-06 11:03:13 -05:00
Sergio Garcia 43efabef6c fix(docker): add g++ to Dockerfile for presidio-analyzer compatibility (#5645) 2024-11-06 10:45:16 -05:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> e73fc14f62 chore(deps): bump trufflesecurity/trufflehog from 3.83.2 to 3.83.3 (#5647)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 10:13:09 +01:00
Sergio Garcia 89fe8fa8e2 chore(version): update Prowler version (#5642) 2024-11-06 08:11:13 +01:00
Drew Kerrigan 634ef2e599 fix(docs): Update misc tutorial categories example (#5644) 2024-11-05 15:37:20 -05:00
Sergio Garcia 4efb70a508 chore(README): update summary table (#5633) 2024-11-05 13:24:46 -05:00
Pepe Fagoaga c3ae0aa873 fix(connection): return Connection on generic exception (#5636) 2024-11-05 12:24:18 -05:00
Sergio Garcia a109cd2816 fix(gcp): do not require organization id to get projects (#5637) 2024-11-05 12:24:07 -05:00
sansns-aws 78fb540bbb feat(rds): add rds_cluster_protected_by_backup_plan check (#5638) 2024-11-05 11:30:45 -05:00
sansns-aws 5b543bf058 feat(aws): Update check metadata with redudancy category (#5640) 2024-11-05 11:27:24 -05:00
264 changed files with 18238 additions and 2044 deletions
+39 -2
View File
@@ -5,6 +5,7 @@
version: 2
updates:
# v5
- package-ecosystem: "pip"
directory: "/"
schedule:
@@ -14,6 +15,7 @@ updates:
labels:
- "dependencies"
- "pip"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
@@ -24,20 +26,55 @@ updates:
- "dependencies"
- "github_actions"
- package-ecosystem: "pip"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
target-branch: master
labels:
- "dependencies"
- "npm"
# v4.6
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
target-branch: v4.6
labels:
- "dependencies"
- "pip"
- "v4"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
target-branch: v4.6
labels:
- "dependencies"
- "github_actions"
- "v4"
# v3
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
target-branch: v3
labels:
- "dependencies"
- "pip"
- "v3"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
interval: "monthly"
open-pull-requests-limit: 10
target-branch: v3
labels:
+7
View File
@@ -79,3 +79,10 @@ output/csv:
- changed-files:
- any-glob-to-any-file: "prowler/lib/outputs/csv/**"
- any-glob-to-any-file: "tests/lib/outputs/csv/**"
compliance:
- changed-files:
- any-glob-to-any-file: "prowler/lib/outputs/compliance/**"
- any-glob-to-any-file: "tests/lib/outputs/compliance/**"
- any-glob-to-any-file: "prowler/compliance/**"
@@ -3,7 +3,11 @@ name: build-lint-push-containers
on:
push:
branches:
# For `v3-latest`
- "v3"
# For `v4-latest`
- "v4.6"
# For `latest`
- "master"
paths-ignore:
- ".github/**"
@@ -80,8 +84,8 @@ jobs:
;;
4)
echo "LATEST_TAG=latest" >> "${GITHUB_ENV}"
echo "STABLE_TAG=stable" >> "${GITHUB_ENV}"
echo "LATEST_TAG=v4-latest" >> "${GITHUB_ENV}"
echo "STABLE_TAG=v4-stable" >> "${GITHUB_ENV}"
;;
*)
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
with:
fetch-depth: 0
- name: TruffleHog OSS
uses: trufflesecurity/trufflehog@v3.83.2
uses: trufflesecurity/trufflehog@v3.85.0
with:
path: ./
base: ${{ github.event.repository.default_branch }}
+1 -1
View File
@@ -91,6 +91,6 @@ jobs:
poetry run pytest -n auto --cov=./prowler --cov-report=xml tests
- name: Upload coverage reports to Codecov
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+5 -5
View File
@@ -10,13 +10,13 @@
</p>
<p align="center">
<a href="https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog"><img width="30" height="30" alt="Prowler community on Slack" src="https://github.com/prowler-cloud/prowler/assets/38561120/3c8b4ec5-6849-41a5-b5e1-52bbb94af73a"></a>
<a href="https://goto.prowler.com/slack"><img width="30" height="30" alt="Prowler community on Slack" src="https://github.com/prowler-cloud/prowler/assets/38561120/3c8b4ec5-6849-41a5-b5e1-52bbb94af73a"></a>
<br>
<a href="https://join.slack.com/t/prowler-workspace/shared_invite/zt-2oinmgmw6-cl7gOrljSEqo_aoripVPFA">Join our Prowler community!</a>
<a href="https://goto.prowler.com/slack">Join our Prowler community!</a>
</p>
<hr>
<p align="center">
<a href="https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog"><img alt="Slack Shield" src="https://img.shields.io/badge/slack-prowler-brightgreen.svg?logo=slack"></a>
<a href="https://goto.prowler.com/slack"><img alt="Slack Shield" src="https://img.shields.io/badge/slack-prowler-brightgreen.svg?logo=slack"></a>
<a href="https://pypi.org/project/prowler/"><img alt="Python Version" src="https://img.shields.io/pypi/v/prowler.svg"></a>
<a href="https://pypi.python.org/pypi/prowler/"><img alt="Python Version" src="https://img.shields.io/pypi/pyversions/prowler.svg"></a>
<a href="https://pypistats.org/packages/prowler"><img alt="PyPI Prowler Downloads" src="https://img.shields.io/pypi/dw/prowler.svg?label=prowler%20downloads"></a>
@@ -64,8 +64,8 @@ It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, Fe
| Provider | Checks | Services | [Compliance Frameworks](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/compliance/) | [Categories](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/misc/#categories) |
|---|---|---|---|---|
| AWS | 553 | 77 -> `prowler aws --list-services` | 30 -> `prowler aws --list-compliance` | 9 -> `prowler aws --list-categories` |
| GCP | 77 | 13 -> `prowler gcp --list-services` | 2 -> `prowler gcp --list-compliance` | 2 -> `prowler gcp --list-categories`|
| Azure | 138 | 17 -> `prowler azure --list-services` | 3 -> `prowler azure --list-compliance` | 2 -> `prowler azure --list-categories` |
| GCP | 77 | 13 -> `prowler gcp --list-services` | 3 -> `prowler gcp --list-compliance` | 2 -> `prowler gcp --list-categories`|
| Azure | 138 | 17 -> `prowler azure --list-services` | 4 -> `prowler azure --list-compliance` | 2 -> `prowler azure --list-categories` |
| Kubernetes | 83 | 7 -> `prowler kubernetes --list-services` | 1 -> `prowler kubernetes --list-compliance` | 7 -> `prowler kubernetes --list-categories` |
# 💻 Installation
+36
View File
@@ -0,0 +1,36 @@
import warnings
from dashboard.common_methods import get_section_containers_ens
warnings.filterwarnings("ignore")
def get_table(data):
# append the requirements_description to idgrupocontrol
data["REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL"] = (
data["REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL"]
+ " - "
+ data["REQUIREMENTS_DESCRIPTION"]
)
aux = data[
[
"REQUIREMENTS_ATTRIBUTES_MARCO",
"REQUIREMENTS_ATTRIBUTES_CATEGORIA",
"REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL",
"REQUIREMENTS_ATTRIBUTES_TIPO",
"CHECKID",
"STATUS",
"REGION",
"ACCOUNTID",
"RESOURCEID",
]
]
return get_section_containers_ens(
aux,
"REQUIREMENTS_ATTRIBUTES_MARCO",
"REQUIREMENTS_ATTRIBUTES_CATEGORIA",
"REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL",
"REQUIREMENTS_ATTRIBUTES_TIPO",
)
+36
View File
@@ -0,0 +1,36 @@
import warnings
from dashboard.common_methods import get_section_containers_ens
warnings.filterwarnings("ignore")
def get_table(data):
# append the requirements_description to idgrupocontrol
data["REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL"] = (
data["REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL"]
+ " - "
+ data["REQUIREMENTS_DESCRIPTION"]
)
aux = data[
[
"REQUIREMENTS_ATTRIBUTES_MARCO",
"REQUIREMENTS_ATTRIBUTES_CATEGORIA",
"REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL",
"REQUIREMENTS_ATTRIBUTES_TIPO",
"CHECKID",
"STATUS",
"REGION",
"ACCOUNTID",
"RESOURCEID",
]
]
return get_section_containers_ens(
aux,
"REQUIREMENTS_ATTRIBUTES_MARCO",
"REQUIREMENTS_ATTRIBUTES_CATEGORIA",
"REQUIREMENTS_ATTRIBUTES_IDGRUPOCONTROL",
"REQUIREMENTS_ATTRIBUTES_TIPO",
)
+19 -7
View File
@@ -148,6 +148,7 @@ else:
select_account_dropdown_list = ["All"]
# Append to the list the unique values of the columns ACCOUNTID, PROJECTID and SUBSCRIPTIONID if they exist
if "ACCOUNTID" in data.columns:
data["ACCOUNTID"] = data["ACCOUNTID"].astype(str)
select_account_dropdown_list = select_account_dropdown_list + list(
data["ACCOUNTID"].unique()
)
@@ -246,9 +247,11 @@ def display_data(
dfs = []
for file in files:
df = pd.read_csv(
file, sep=";", on_bad_lines="skip", encoding=encoding_format
file, sep=";", on_bad_lines="skip", encoding=encoding_format, dtype=str
)
dfs.append(df.astype(str))
df = df.astype(str).fillna("nan")
df.columns = df.columns.astype(str)
dfs.append(df)
return pd.concat(dfs, ignore_index=True)
data = load_csv_files(files)
@@ -274,17 +277,24 @@ def display_data(
data.rename(columns={"PROJECTID": "ACCOUNTID"}, inplace=True)
data["REGION"] = "-"
# Rename the column SUBSCRIPTIONID to ACCOUNTID for Azure
if data.columns.str.contains("SUBSCRIPTIONID").any():
if (
data.columns.str.contains("SUBSCRIPTIONID").any()
and not data.columns.str.contains("ACCOUNTID").any()
):
data.rename(columns={"SUBSCRIPTIONID": "ACCOUNTID"}, inplace=True)
data["REGION"] = "-"
# Handle v3 azure cis compliance
if data.columns.str.contains("SUBSCRIPTION").any():
if (
data.columns.str.contains("SUBSCRIPTION").any()
and not data.columns.str.contains("ACCOUNTID").any()
):
data.rename(columns={"SUBSCRIPTION": "ACCOUNTID"}, inplace=True)
data["REGION"] = "-"
# Filter ACCOUNT
if account_filter == ["All"]:
updated_cloud_account_values = data["ACCOUNTID"].unique()
elif "All" in account_filter and len(account_filter) > 1:
# Remove 'All' from the list
account_filter.remove("All")
@@ -299,9 +309,11 @@ def display_data(
account_filter_options = list(data["ACCOUNTID"].unique())
account_filter_options = account_filter_options + ["All"]
for item in account_filter_options:
if "nan" in item or item.__class__.__name__ != "str" or item is None:
account_filter_options.remove(item)
account_filter_options = [
item
for item in account_filter_options
if isinstance(item, str) and item.lower() != "nan"
]
# Filter REGION
if region_filter_analytics == ["All"]:
+1 -1
View File
@@ -2,7 +2,7 @@
For technical support or any type of inquiries, you are very welcome to:
- Reach out to community members on the [**Prowler Slack channel**](https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog)
- Reach out to community members on the [**Prowler Slack channel**](https://goto.prowler.com/slack)
- Open an Issue or a Pull Request in our [**GitHub repository**](https://github.com/prowler-cloud/prowler).
+14 -8
View File
@@ -160,14 +160,20 @@ else:
All the checks MUST fill the `report.resource_id` and `report.resource_arn` with the following criteria:
- AWS
- Resource ID -- `report.resource_id`
- AWS Account --> Account Number `123456789012`
- AWS Resource --> Resource ID / Name
- Root resource --> `<root_account>`
- Resource ARN -- `report.resource_arn`
- AWS Account --> Root ARN `arn:aws:iam::123456789012:root`
- AWS Resource --> Resource ARN
- Root resource --> Resource Type ARN `f"arn:{service_client.audited_partition}:<service_name>:{service_client.region}:{service_client.audited_account}:<resource_type>"`
- Resouce ID and resource ARN:
- If the resource audited is the AWS account:
- `resource_id` -> AWS Account Number
- `resource_arn` -> AWS Account Root ARN
- If we cant get the ARN from the resource audited, we create a valid ARN with the `resource_id` part as the resource audited. Examples:
- Bedrock -> `arn:<partition>:bedrock:<region>:<account-id>:model-invocation-logging`
- DirectConnect -> `arn:<partition>:directconnect:<region>:<account-id>:dxcon`
- If there is no real resource to audit we do the following:
- resource_id -> `resource_type/unknown`
- resource_arn -> `arn:<partition>:<service>:<region>:<account-id>:<resource_type>/unknown`
- Examples:
- AWS Security Hub -> `arn:<partition>:security-hub:<region>:<account-id>:hub/unknown`
- Access Analyzer -> `arn:<partition>:access-analyzer:<region>:<account-id>:analyzer/unknown`
- GuardDuty -> `arn:<partition>:guardduty:<region>:<account-id>:detector/unknown`
- GCP
- Resource ID -- `report.resource_id`
- GCP Resource --> Resource ID
+1 -1
View File
@@ -67,4 +67,4 @@ If you create or review a PR in https://github.com/prowler-cloud/prowler please
## Want some swag as appreciation for your contribution?
If you are like us and you love swag, we are happy to thank you for your contribution with some laptop stickers or whatever other swag we may have at that time. Please, tell us more details and your pull request link in our [Slack workspace here](https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog). You can also reach out to Toni de la Fuente on Twitter [here](https://twitter.com/ToniBlyx), his DMs are open.
If you are like us and you love swag, we are happy to thank you for your contribution with some laptop stickers or whatever other swag we may have at that time. Please, tell us more details and your pull request link in our [Slack workspace here](https://goto.prowler.com/slack). You can also reach out to Toni de la Fuente on Twitter [here](https://twitter.com/ToniBlyx), his DMs are open.
+9 -9
View File
@@ -190,18 +190,18 @@ from prowler.providers.common.models import Audit_Metadata
from prowler.providers.common.provider import Provider
from prowler.providers.<new_provider_name>.models import (
# All providers models needed
ProvierSessionModel,
ProvierIdentityModel,
ProvierOutputOptionsModel
ProviderSessionModel,
ProviderIdentityModel,
ProviderOutputOptionsModel
)
class NewProvider(Provider):
# All properties from the class, some of this are properties in the base class
_type: str = "<provider_name>"
_session: <ProvierSessionModel>
_identity: <ProvierIdentityModel>
_session: <ProviderSessionModel>
_identity: <ProviderIdentityModel>
_audit_config: dict
_output_options: ProvierOutputOptionsModel
_output_options: ProviderOutputOptionsModel
_mutelist: dict
audit_metadata: Audit_Metadata
@@ -212,13 +212,13 @@ class NewProvider(Provider):
arguments (dict): A dictionary containing configuration arguments.
"""
logger.info("Setting <NewProviderName> provider ...")
# First get from arguments the necesary from the cloud acount (subscriptions or projects or whatever the provider use for storing services)
# First get from arguments the necessary from the cloud account (subscriptions or projects or whatever the provider use for storing services)
# Set the session with the method enforced by parent class
self._session = self.setup_session(credentials_file)
# Set the Identity class normaly the provider class give by Python provider library
self._identity = <ProvierIdentityModel>()
self._identity = <ProviderIdentityModel>()
# Set the provider configuration
self._audit_config = load_and_validate_config_file(
@@ -254,7 +254,7 @@ class NewProvider(Provider):
<all_needed_for_auth> Can include all necessary arguments to setup the session
Returns:
Credentials necesary to communicate with the provider.
Credentials necessary to communicate with the provider.
"""
pass
+1 -1
View File
@@ -125,5 +125,5 @@ prowler <provider> --list-categories
```
- Execute specific category(s):
```console
prowler <provider> --categories
prowler <provider> --categories secrets
```
+36
View File
@@ -0,0 +1,36 @@
# Prowler Check Kreator
???+ note
Currently, this tool is only available for creating checks for the AWS provider.
**Prowler Check Kreator** is a utility designed to streamline the creation of new checks for Prowler. This tool generates all necessary files required to add a new check to the Prowler repository. Specifically, it creates:
- A dedicated folder for the check.
- The main check script.
- A metadata file with essential details.
- A folder and file structure for testing the check.
## Usage
To use the tool, execute the main script with the following command:
```bash
python util/prowler_check_kreator/prowler_check_kreator.py <prowler_provider> <check_name>
```
Parameters:
- `<prowler_provider>`: Currently only AWS is supported.
- `<check_name>`: The name you wish to assign to the new check.
## AI integration
This tool optionally integrates AI to assist in generating the check code and metadata file content. When AI assistance is chosen, the tool uses [Gemini](https://gemini.google.com/) to produce preliminary code and metadata.
???+ note
For this feature to work, you must have the library `google-generativeai` installed in your Python environment.
???+ warning
AI-generated code and metadata might contain errors or require adjustments to align with specific Prowler requirements. Carefully review all AI-generated content before committing.
To enable AI assistance, simply confirm when prompted by the tool. Additionally, ensure that the `GEMINI_API_KEY` environment variable is set with a valid Gemini API key. For instructions on obtaining your API key, refer to the [Gemini documentation](https://ai.google.dev/gemini-api/docs/api-key).
+1
View File
@@ -65,6 +65,7 @@ nav:
- Pentesting: tutorials/pentesting.md
- Parallel Execution: tutorials/parallel-execution.md
- Developer Guide: developer-guide/introduction.md
- Prowler Check Kreator: tutorials/prowler-check-kreator.md
- AWS:
- Authentication: tutorials/aws/authentication.md
- Assume Role: tutorials/aws/role-assumption.md
Generated
+1177 -1055
View File
File diff suppressed because it is too large Load Diff
+58 -1
View File
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import sys
import threading
from os import environ
from colorama import Fore, Style
@@ -10,11 +11,16 @@ from colorama import init as colorama_init
from prowler.config.config import (
csv_file_suffix,
get_available_compliance_frameworks,
get_available_update,
html_file_suffix,
json_asff_file_suffix,
json_ocsf_file_suffix,
)
from prowler.lib.banner import print_banner
from prowler.lib.banner import (
print_banner,
print_prowler_cloud_banner,
print_update_notice,
)
from prowler.lib.check.check import (
exclude_checks_to_run,
exclude_services_to_run,
@@ -53,6 +59,8 @@ from prowler.lib.outputs.compliance.cis.cis_gcp import GCPCIS
from prowler.lib.outputs.compliance.cis.cis_kubernetes import KubernetesCIS
from prowler.lib.outputs.compliance.compliance import display_compliance_table
from prowler.lib.outputs.compliance.ens.ens_aws import AWSENS
from prowler.lib.outputs.compliance.ens.ens_azure import AzureENS
from prowler.lib.outputs.compliance.ens.ens_gcp import GCPENS
from prowler.lib.outputs.compliance.generic.generic import GenericCompliance
from prowler.lib.outputs.compliance.iso27001.iso27001_aws import AWSISO27001
from prowler.lib.outputs.compliance.kisa_ismsp.kisa_ismsp_aws import AWSKISAISMSP
@@ -116,6 +124,18 @@ def prowler():
if args.no_color:
colorama_init(strip=True)
# Check in the background whether a newer Prowler release is available;
# the result is printed at the end of the scan so the check never adds
# latency. Skipped with --no-banner/--only-logs and via
# PROWLER_NO_VERSION_CHECK/DO_NOT_TRACK (handled in get_available_update).
available_update = {}
update_check_thread = threading.Thread(
target=lambda: available_update.update(latest=get_available_update()),
daemon=True,
)
if not args.no_banner and not args.only_logs:
update_check_thread.start()
if not args.no_banner:
legend = args.verbose or getattr(args, "fixer", None)
print_banner(legend)
@@ -511,6 +531,20 @@ def prowler():
)
generated_outputs["compliance"].append(mitre_attack)
mitre_attack.batch_write_data_to_file()
elif compliance_name.startswith("ens_"):
# Generate ENS Finding Object
filename = (
f"{output_options.output_directory}/compliance/"
f"{output_options.output_filename}_{compliance_name}.csv"
)
ens = AzureENS(
findings=finding_outputs,
compliance=bulk_compliance_frameworks[compliance_name],
create_file_descriptor=True,
file_path=filename,
)
generated_outputs["compliance"].append(ens)
ens.batch_write_data_to_file()
else:
filename = (
f"{output_options.output_directory}/compliance/"
@@ -555,6 +589,20 @@ def prowler():
)
generated_outputs["compliance"].append(mitre_attack)
mitre_attack.batch_write_data_to_file()
elif compliance_name.startswith("ens_"):
# Generate ENS Finding Object
filename = (
f"{output_options.output_directory}/compliance/"
f"{output_options.output_filename}_{compliance_name}.csv"
)
ens = GCPENS(
findings=finding_outputs,
compliance=bulk_compliance_frameworks[compliance_name],
create_file_descriptor=True,
file_path=filename,
)
generated_outputs["compliance"].append(ens)
ens.batch_write_data_to_file()
else:
filename = (
f"{output_options.output_directory}/compliance/"
@@ -689,6 +737,15 @@ def prowler():
f"\nDetailed compliance results are in {Fore.YELLOW}{output_options.output_directory}/compliance/{Style.RESET_ALL}\n"
)
# Promote Prowler Cloud as the last thing the user sees after the results,
# preceded by an update notice when a newer release is available
if not args.no_banner and not args.only_logs:
if update_check_thread.is_alive():
update_check_thread.join(timeout=2)
if available_update.get("latest"):
print_update_notice(available_update["latest"])
print_prowler_cloud_banner()
# If custom checks were passed, remove the modules
if checks_folder:
remove_custom_checks_module(checks_folder, provider)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+34 -7
View File
@@ -12,7 +12,7 @@ from prowler.lib.logger import logger
timestamp = datetime.today()
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc)
prowler_version = "4.5.4"
prowler_version = "4.6.3"
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"
@@ -86,23 +86,50 @@ def get_default_mute_file_path(provider: str):
return mutelist_path
def check_current_version():
def get_latest_release_version():
"""Return the latest Prowler release tag name from GitHub, or None if it cannot be retrieved."""
try:
prowler_version_string = f"Prowler {prowler_version}"
release_response = requests.get(
"https://api.github.com/repos/prowler-cloud/prowler/tags", timeout=1
)
latest_version = release_response.json()[0]["name"]
return release_response.json()[0]["name"]
except Exception:
return None
def get_available_update():
"""Return the latest Prowler version if it is newer than the running one, None otherwise.
Honors the PROWLER_NO_VERSION_CHECK and DO_NOT_TRACK environment variables:
when either is set, no network call is made and None is returned.
"""
if os.environ.get("PROWLER_NO_VERSION_CHECK") or os.environ.get("DO_NOT_TRACK"):
return None
latest_version = get_latest_release_version()
try:
if latest_version and version.parse(latest_version) > version.parse(
prowler_version
):
return latest_version
except Exception:
return None
return None
def check_current_version():
prowler_version_string = f"Prowler {prowler_version}"
latest_version = get_latest_release_version()
if not latest_version:
return prowler_version_string
try:
if version.parse(latest_version) > version.parse(prowler_version):
return f"{prowler_version_string} (latest is {latest_version}, upgrade for the latest features)"
else:
return (
f"{prowler_version_string} (You are running the latest version, yay!)"
)
except requests.RequestException:
return f"{prowler_version_string}"
except Exception:
return f"{prowler_version_string}"
return prowler_version_string
def load_and_validate_config_file(provider: str, config_file_path: str) -> dict:
+5
View File
@@ -368,6 +368,11 @@ aws:
# Maximum number of days a secret should be rotated
max_days_secret_unrotated: 90
# AWS Kinesis Configuration
# Minimum retention period in hours for Kinesis streams
min_kinesis_stream_retention_hours: 168 # 7 days
# Azure Configuration
azure:
# Azure Network Configuration
+69
View File
@@ -2,6 +2,75 @@ from colorama import Fore, Style
from prowler.config.config import banner_color, orange_color, prowler_version, timestamp
# Prowler Cloud landing URL used by the CLI banner. The visible text stays
# "cloud.prowler.com" while the clickable target carries the UTM parameters so
# terminals that support OSC 8 hyperlinks attribute the visit to the v4 CLI.
CLOUD_DISPLAY_TEXT = "cloud.prowler.com"
CLOUD_BANNER_URL = (
"https://cloud.prowler.com/sign-up?utm_source=prowler-cli&utm_content=v4"
)
def _hyperlink(url: str, text: str) -> str:
"""Wrap ``text`` in an OSC 8 terminal hyperlink pointing to ``url``.
Terminals that support OSC 8 render ``text`` as a clickable link to ``url``;
those that do not simply display ``text`` unchanged.
"""
return f"\033]8;;{url}\033\\{text}\033]8;;\033\\"
def print_update_notice(latest_version: str):
"""
Prints a notice that a newer Prowler version is available.
Parameters:
- latest_version (str): The latest released Prowler version.
Returns:
- None
"""
print(
f"\n{Fore.YELLOW}A new version of Prowler is available: {prowler_version}{latest_version}{Style.RESET_ALL}\n"
f"Upgrading from Prowler v4 is a major version upgrade — see the release notes at\n"
f"https://github.com/prowler-cloud/prowler/releases before upgrading.\n"
f"Upgrade with: {Style.BRIGHT}pipx upgrade prowler{Style.RESET_ALL} "
f"(disable this check with PROWLER_NO_VERSION_CHECK=1)"
)
def print_prowler_cloud_banner():
"""
Prints a promotional banner highlighting what Prowler Cloud adds on top of
the open-source CLI.
Shown at the end of a scan to let users know about the managed platform
capabilities they are missing.
Returns:
- None
"""
check = f"{Fore.GREEN}{Style.RESET_ALL}"
bar = f"{banner_color}{Style.RESET_ALL}"
print(
f"""
{bar} {Style.BRIGHT}You're getting a snapshot 📸. Prowler Cloud gives you the full picture:{Style.RESET_ALL}
{bar}
{bar} {check} {Style.BRIGHT}Send your findings{Style.RESET_ALL} - directly from the Prowler CLI to Prowler Cloud.
{bar} {check} {Style.BRIGHT}Continuous Security Monitoring{Style.RESET_ALL} - custom scheduling and scan configuration with history, trends and alerts.
{bar} {check} {Style.BRIGHT}Triage{Style.RESET_ALL} - review findings, flag false positives and track accepted risk with your team.
{bar} {check} {Style.BRIGHT}Lighthouse AI + MCP{Style.RESET_ALL} - autonomous triage, custom dashboards, prioritization with prevention and remediation.
{bar} {check} {Style.BRIGHT}Alerts{Style.RESET_ALL} - get notified when anything you want is happening.
{bar} {check} {Style.BRIGHT}Live Compliance{Style.RESET_ALL} - dashboards for 50+ frameworks, always up to date.
{bar} {check} {Style.BRIGHT}Remediation{Style.RESET_ALL} - complete guided remediation including Autonomous remediation with Lighthouse AI.
{bar} {check} {Style.BRIGHT}Attack Path Visualization{Style.RESET_ALL} - see how attackers chain risks to reach your crown jewels.
{bar} {check} {Style.BRIGHT}Bulk Provisioning{Style.RESET_ALL} - add your entire AWS Organization in seconds.
{bar} {check} {Style.BRIGHT}Integrations{Style.RESET_ALL} - Anything with our MCP + Jira, Slack, AWS Security Hub, Amazon S3, SSO and RBAC.
{bar}
{bar} {banner_color}Start free at 👉 {_hyperlink(CLOUD_BANNER_URL, CLOUD_DISPLAY_TEXT)}{Style.RESET_ALL}
"""
)
def print_banner(legend: bool = False):
"""
+1 -1
View File
@@ -111,7 +111,7 @@ def load_checks_to_execute(
):
checks_to_execute.add(check_name)
# Only execute threat detection checks if threat-detection category is set
if categories and categories != [] and "threat-detection" not in categories:
if not categories or "threat-detection" not in categories:
for threat_detection_check in check_categories.get("threat-detection", []):
checks_to_execute.discard(threat_detection_check)
+3 -2
View File
@@ -322,8 +322,9 @@ class CheckMetadata(BaseModel):
checks = set()
if service:
if service == "lambda":
service = "awslambda"
# This is a special case for the AWS provider since `lambda` is a reserved keyword in Python
if service == "awslambda":
service = "lambda"
checks = {
check_name
for check_name, check_metadata in bulk_checks_metadata.items()
+4 -3
View File
@@ -94,11 +94,12 @@ def get_cis_table(
print(
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
)
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
overview_table = [
[
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
]
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
+5 -4
View File
@@ -30,7 +30,7 @@ def get_ens_table(
check = bulk_checks_metadata[finding.check_metadata.CheckID]
check_compliances = check.Compliance
for compliance in check_compliances:
if compliance.Framework == "ENS" and compliance.Provider == "AWS":
if compliance.Framework == "ENS":
for requirement in compliance.Requirements:
for attribute in requirement.Attributes:
marco_categoria = f"{attribute.Marco}/{attribute.Categoria}"
@@ -95,11 +95,12 @@ def get_ens_table(
print(
f"\nEstado de Cumplimiento de {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL}:"
)
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
overview_table = [
[
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) NO CUMPLE{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) CUMPLE{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) NO CUMPLE{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) CUMPLE{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
]
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
@@ -0,0 +1,103 @@
from prowler.lib.check.compliance_models import Compliance
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
from prowler.lib.outputs.compliance.ens.models import AzureENSModel
from prowler.lib.outputs.finding import Finding
class AzureENS(ComplianceOutput):
"""
This class represents the Azure ENS compliance output.
Attributes:
- _data (list): A list to store transformed data from findings.
- _file_descriptor (TextIOWrapper): A file descriptor to write data to a file.
Methods:
- transform: Transforms findings into Azure ENS compliance format.
"""
def transform(
self,
findings: list[Finding],
compliance: Compliance,
compliance_name: str,
) -> None:
"""
Transforms a list of findings into AWS ENS compliance format.
Parameters:
- findings (list): A list of findings.
- compliance (Compliance): A compliance model.
- compliance_name (str): The name of the compliance model.
Returns:
- None
"""
for finding in findings:
# Get the compliance requirements for the finding
finding_requirements = finding.compliance.get(compliance_name, [])
for requirement in compliance.Requirements:
if requirement.Id in finding_requirements:
for attribute in requirement.Attributes:
compliance_row = AzureENSModel(
Provider=finding.provider,
Description=compliance.Description,
SubscriptionId=finding.account_name,
Location=finding.region,
AssessmentDate=str(finding.timestamp),
Requirements_Id=requirement.Id,
Requirements_Description=requirement.Description,
Requirements_Attributes_IdGrupoControl=attribute.IdGrupoControl,
Requirements_Attributes_Marco=attribute.Marco,
Requirements_Attributes_Categoria=attribute.Categoria,
Requirements_Attributes_DescripcionControl=attribute.DescripcionControl,
Requirements_Attributes_Nivel=attribute.Nivel,
Requirements_Attributes_Tipo=attribute.Tipo,
Requirements_Attributes_Dimensiones=",".join(
attribute.Dimensiones
),
Requirements_Attributes_ModoEjecucion=attribute.ModoEjecucion,
Requirements_Attributes_Dependencias=",".join(
attribute.Dependencias
),
Status=finding.status,
StatusExtended=finding.status_extended,
ResourceId=finding.resource_uid,
ResourceName=finding.resource_name,
CheckId=finding.check_id,
Muted=finding.muted,
)
self._data.append(compliance_row)
# Add manual requirements to the compliance output
for requirement in compliance.Requirements:
if not requirement.Checks:
for attribute in requirement.Attributes:
compliance_row = AzureENSModel(
Provider=compliance.Provider.lower(),
Description=compliance.Description,
SubscriptionId="",
Location="",
AssessmentDate=str(finding.timestamp),
Requirements_Id=requirement.Id,
Requirements_Description=requirement.Description,
Requirements_Attributes_IdGrupoControl=attribute.IdGrupoControl,
Requirements_Attributes_Marco=attribute.Marco,
Requirements_Attributes_Categoria=attribute.Categoria,
Requirements_Attributes_DescripcionControl=attribute.DescripcionControl,
Requirements_Attributes_Nivel=attribute.Nivel,
Requirements_Attributes_Tipo=attribute.Tipo,
Requirements_Attributes_Dimensiones=",".join(
attribute.Dimensiones
),
Requirements_Attributes_ModoEjecucion=attribute.ModoEjecucion,
Requirements_Attributes_Dependencias=",".join(
attribute.Dependencias
),
Status="MANUAL",
StatusExtended="Manual check",
ResourceId="manual_check",
ResourceName="Manual check",
CheckId="manual",
Muted=False,
)
self._data.append(compliance_row)
@@ -0,0 +1,103 @@
from prowler.lib.check.compliance_models import Compliance
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
from prowler.lib.outputs.compliance.ens.models import GCPENSModel
from prowler.lib.outputs.finding import Finding
class GCPENS(ComplianceOutput):
"""
This class represents the GCP ENS compliance output.
Attributes:
- _data (list): A list to store transformed data from findings.
- _file_descriptor (TextIOWrapper): A file descriptor to write data to a file.
Methods:
- transform: Transforms findings into GCP ENS compliance format.
"""
def transform(
self,
findings: list[Finding],
compliance: Compliance,
compliance_name: str,
) -> None:
"""
Transforms a list of findings into AWS ENS compliance format.
Parameters:
- findings (list): A list of findings.
- compliance (Compliance): A compliance model.
- compliance_name (str): The name of the compliance model.
Returns:
- None
"""
for finding in findings:
# Get the compliance requirements for the finding
finding_requirements = finding.compliance.get(compliance_name, [])
for requirement in compliance.Requirements:
if requirement.Id in finding_requirements:
for attribute in requirement.Attributes:
compliance_row = GCPENSModel(
Provider=finding.provider,
Description=compliance.Description,
ProjectId=finding.account_uid,
Location=finding.region,
AssessmentDate=str(finding.timestamp),
Requirements_Id=requirement.Id,
Requirements_Description=requirement.Description,
Requirements_Attributes_IdGrupoControl=attribute.IdGrupoControl,
Requirements_Attributes_Marco=attribute.Marco,
Requirements_Attributes_Categoria=attribute.Categoria,
Requirements_Attributes_DescripcionControl=attribute.DescripcionControl,
Requirements_Attributes_Nivel=attribute.Nivel,
Requirements_Attributes_Tipo=attribute.Tipo,
Requirements_Attributes_Dimensiones=",".join(
attribute.Dimensiones
),
Requirements_Attributes_ModoEjecucion=attribute.ModoEjecucion,
Requirements_Attributes_Dependencias=",".join(
attribute.Dependencias
),
Status=finding.status,
StatusExtended=finding.status_extended,
ResourceId=finding.resource_uid,
ResourceName=finding.resource_name,
CheckId=finding.check_id,
Muted=finding.muted,
)
self._data.append(compliance_row)
# Add manual requirements to the compliance output
for requirement in compliance.Requirements:
if not requirement.Checks:
for attribute in requirement.Attributes:
compliance_row = GCPENSModel(
Provider=compliance.Provider.lower(),
Description=compliance.Description,
ProjectId="",
Location="",
AssessmentDate=str(finding.timestamp),
Requirements_Id=requirement.Id,
Requirements_Description=requirement.Description,
Requirements_Attributes_IdGrupoControl=attribute.IdGrupoControl,
Requirements_Attributes_Marco=attribute.Marco,
Requirements_Attributes_Categoria=attribute.Categoria,
Requirements_Attributes_DescripcionControl=attribute.DescripcionControl,
Requirements_Attributes_Nivel=attribute.Nivel,
Requirements_Attributes_Tipo=attribute.Tipo,
Requirements_Attributes_Dimensiones=",".join(
attribute.Dimensiones
),
Requirements_Attributes_ModoEjecucion=attribute.ModoEjecucion,
Requirements_Attributes_Dependencias=",".join(
attribute.Dependencias
),
Status="MANUAL",
StatusExtended="Manual check",
ResourceId="manual_check",
ResourceName="Manual check",
CheckId="manual",
Muted=False,
)
self._data.append(compliance_row)
@@ -28,3 +28,61 @@ class AWSENSModel(BaseModel):
CheckId: str
Muted: bool
ResourceName: str
class AzureENSModel(BaseModel):
"""
AzureENSModel generates a finding's output in CSV ENS format for Azure.
"""
Provider: str
Description: str
SubscriptionId: str
Location: str
AssessmentDate: str
Requirements_Id: str
Requirements_Description: str
Requirements_Attributes_IdGrupoControl: str
Requirements_Attributes_Marco: str
Requirements_Attributes_Categoria: str
Requirements_Attributes_DescripcionControl: str
Requirements_Attributes_Nivel: str
Requirements_Attributes_Tipo: str
Requirements_Attributes_Dimensiones: str
Requirements_Attributes_ModoEjecucion: str
Requirements_Attributes_Dependencias: str
Status: str
StatusExtended: str
ResourceId: str
CheckId: str
Muted: bool
ResourceName: str
class GCPENSModel(BaseModel):
"""
GCPENSModel generates a finding's output in CSV ENS format for GCP.
"""
Provider: str
Description: str
ProjectId: str
Location: str
AssessmentDate: str
Requirements_Id: str
Requirements_Description: str
Requirements_Attributes_IdGrupoControl: str
Requirements_Attributes_Marco: str
Requirements_Attributes_Categoria: str
Requirements_Attributes_DescripcionControl: str
Requirements_Attributes_Nivel: str
Requirements_Attributes_Tipo: str
Requirements_Attributes_Dimensiones: str
Requirements_Attributes_ModoEjecucion: str
Requirements_Attributes_Dependencias: str
Status: str
StatusExtended: str
ResourceId: str
CheckId: str
Muted: bool
ResourceName: str
@@ -39,11 +39,12 @@ def get_generic_compliance_table(
print(
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
)
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
overview_table = [
[
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
]
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
@@ -61,11 +61,12 @@ def get_kisa_ismsp_table(
print(
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
)
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
overview_table = [
[
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
]
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
@@ -69,11 +69,12 @@ def get_mitre_attack_table(
print(
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
)
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
overview_table = [
[
f"{Fore.RED}{round(len(fail_count) / len(findings) * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / len(findings) * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / len(findings) * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
]
]
print(tabulate(overview_table, tablefmt="rounded_grid"))
@@ -0,0 +1,231 @@
from prowler.exceptions.exceptions import ProwlerException
# Exceptions codes from 9000 to 9999 are reserved for Jira exceptions
class JiraBaseException(ProwlerException):
"""Base class for Jira exceptions."""
JIRA_ERROR_CODES = {
(9000, "JiraNoProjectsError"): {
"message": "No projects were found in Jira.",
"remediation": "Please create a project in Jira.",
},
(9001, "JiraAuthenticationError"): {
"message": "Failed to authenticate with Jira.",
"remediation": "Please check the connection settings and permissions and try again. Needed scopes are: read:jira-user read:jira-work write:jira-work",
},
(9002, "JiraTestConnectionError"): {
"message": "Failed to connect to Jira.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9003, "JiraCreateIssueError"): {
"message": "Failed to create an issue in Jira.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9004, "JiraGetProjectsError"): {
"message": "Failed to get projects from Jira.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9005, "JiraGetCloudIDError"): {
"message": "Failed to get the cloud ID from Jira.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9006, "JiraGetCloudIDNoResourcesError"): {
"message": "No resources were found in Jira.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9007, "JiraGetCloudIDResponseError"): {
"message": "Failed to get the cloud ID from Jira.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9008, "JiraRefreshTokenResponseError"): {
"message": "Failed to refresh the access token, response code did not match 200.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9009, "JiraRefreshTokenError"): {
"message": "Failed to refresh the access token.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9010, "JiraGetAccessTokenError"): {
"message": "Failed to get the access token.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9011, "JiraGetAuthResponseError"): {
"message": "Failed to authenticate with Jira.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9012, "JiraGetProjectsResponseError"): {
"message": "Failed to get projects from Jira, response code did not match 200.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9013, "JiraSendFindingsResponseError"): {
"message": "Failed to send findings to Jira, response code did not match 201.",
"remediation": "Please check the finding format and try again.",
},
(9014, "JiraGetAvailableIssueTypesError"): {
"message": "Failed to get available issue types from Jira.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9015, "JiraGetAvailableIssueTypesResponseError"): {
"message": "Failed to get available issue types from Jira, response code did not match 200.",
"remediation": "Please check the connection settings and permissions and try again.",
},
(9016, "JiraInvalidIssueTypeError"): {
"message": "The issue type is invalid.",
"remediation": "Please check the issue type and try again.",
},
(9017, "JiraNoTokenError"): {
"message": "No token was found.",
"remediation": "Make sure the token is set when using the Jira integration.",
},
(9018, "JiraInvalidProjectKeyError"): {
"message": "The project key is invalid.",
"remediation": "Please check the project key and try again.",
},
}
def __init__(self, code, file=None, original_exception=None, message=None):
module = "Jira"
error_info = self.JIRA_ERROR_CODES.get((code, self.__class__.__name__))
if message:
error_info["message"] = message
super().__init__(
code=code,
source=module,
file=file,
original_exception=original_exception,
error_info=error_info,
)
class JiraNoProjectsError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9000, file=file, original_exception=original_exception, message=message
)
class JiraAuthenticationError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9001, file=file, original_exception=original_exception, message=message
)
class JiraTestConnectionError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9002, file=file, original_exception=original_exception, message=message
)
class JiraCreateIssueError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9003, file=file, original_exception=original_exception, message=message
)
class JiraGetProjectsError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9004, file=file, original_exception=original_exception, message=message
)
class JiraGetCloudIDError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9005, file=file, original_exception=original_exception, message=message
)
class JiraGetCloudIDNoResourcesError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9006, file=file, original_exception=original_exception, message=message
)
class JiraGetCloudIDResponseError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9007, file=file, original_exception=original_exception, message=message
)
class JiraRefreshTokenResponseError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9008, file=file, original_exception=original_exception, message=message
)
class JiraRefreshTokenError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9009, file=file, original_exception=original_exception, message=message
)
class JiraGetAccessTokenError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9010, file=file, original_exception=original_exception, message=message
)
class JiraGetAuthResponseError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9011, file=file, original_exception=original_exception, message=message
)
class JiraGetProjectsResponseError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9012, file=file, original_exception=original_exception, message=message
)
class JiraSendFindingsResponseError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9013, file=file, original_exception=original_exception, message=message
)
class JiraGetAvailableIssueTypesError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9014, file=file, original_exception=original_exception, message=message
)
class JiraGetAvailableIssueTypesResponseError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9015, file=file, original_exception=original_exception, message=message
)
class JiraInvalidIssueTypeError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9016, file=file, original_exception=original_exception, message=message
)
class JiraNoTokenError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9017, file=file, original_exception=original_exception, message=message
)
class JiraInvalidProjectKeyError(JiraBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
9018, file=file, original_exception=original_exception, message=message
)
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -185,7 +185,7 @@ class Slack:
"accessory": {
"type": "button",
"text": {"type": "plain_text", "text": "Prowler :slack:"},
"url": "https://join.slack.com/t/prowler-workspace/shared_invite/zt-1hix76xsl-2uq222JIXrC7Q8It~9ZNog",
"url": "https://goto.prowler.com/slack",
},
},
{
+242 -31
View File
@@ -39,6 +39,7 @@ from prowler.providers.aws.exceptions.exceptions import (
AWSIAMRoleARNPartitionEmptyError,
AWSIAMRoleARNRegionNotEmtpyError,
AWSIAMRoleARNServiceNotIAMnorSTSError,
AWSInvalidPartitionError,
AWSInvalidProviderIdError,
AWSNoCredentialsError,
AWSProfileNotFoundError,
@@ -62,12 +63,32 @@ from prowler.providers.aws.models import (
AWSMFAInfo,
AWSOrganizationsInfo,
AWSSession,
Partition,
)
from prowler.providers.common.models import Audit_Metadata, Connection
from prowler.providers.common.provider import Provider
class AwsProvider(Provider):
"""
AwsProvider class is the main class for the AWS provider.
This class is responsible for initializing the AWS provider, setting up the AWS session, validating the AWS
credentials, assuming an IAM role, getting the AWS Organizations metadata, and setting the AWS identity.
Attributes:
_type (str): The provider type.
_identity (AWSIdentityInfo): The AWS provider identity information.
_session (AWSSession): The AWS provider session.
_organizations_metadata (AWSOrganizationsInfo): The AWS Organizations metadata.
_audit_resources (list): The list of resources to audit.
_audit_config (dict): The audit configuration.
_scan_unused_services (bool): A boolean indicating whether to scan unused services.
_enabled_regions (set): The set of enabled regions.
_mutelist (AWSMutelist): The AWS provider mutelist.
audit_metadata (Audit_Metadata): The audit metadata.
"""
_type: str = "aws"
_identity: AWSIdentityInfo
_session: AWSSession
@@ -106,10 +127,10 @@ class AwsProvider(Provider):
"""
Initializes the AWS provider.
Arguments:
Args:
- retries_max_attempts: The maximum number of retries for the AWS client.
- role_arn: The ARN of the IAM role to assume.
- session_duration: The duration of the session in seconds.
- session_duration: The duration of the session in seconds, between 900 and 43200.
- external_id: The external ID to use when assuming the IAM role.
- role_session_name: The name of the session when assuming the IAM role.
- mfa: A boolean indicating whether MFA is enabled.
@@ -134,6 +155,37 @@ class AwsProvider(Provider):
- ArgumentTypeError: If the input external ID is invalid.
- ArgumentTypeError: If the input role session name is invalid.
Usage:
- Boto3 is used so we follow their credential setup process:
- Authentication: Make sure you have properly configured your AWS CLI with a valid Access Key and Region or declare the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.
- aws configure
or
- export AWS_ACCESS_KEY_ID="ASXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXX"
export AWS_SESSION_TOKEN="XXXXXXXXX"
- To create a new aws object you can use:
- aws = AwsProvider()
- aws = AwsProvider(aws_access_key_id="ASXXXXXXX", aws_secret_access_key="XXXXXXXXX", aws_session_token="XXXXXXXXX")
- Profile: If you have multiple profiles in your AWS CLI configuration, you can specify the profile to use:
- aws = AwsProvider(profile="profile_name")
- MFA: If you have MFA enabled you can specify it:
- aws = AwsProvider(mfa=True)
* Note: If you have MFA enabled you will be prompted to enter the MFA ARN and the MFA TOTP code.
* Note: Take into account that you can use static credentials or a profile, with the combination of MFA.
- Assume Role: *Requires authentication.* Prowler can be used against multiple accounts using IAM Assume Role features depending on each use case:
- Set up a custom profile inside your AWS CLI configuration file:
- [profile profile_name]
role_arn = arn:aws:iam::123456789012:role/role_name
- aws = AwsProvider(profile="profile_name")
- Use role_arn directly:
- aws = AwsProvider(role_arn="arn:aws:iam::123456789012:role/role_name")
- Use role_arn with session duration(in seconds, by default 3600) and external ID:
- aws = AwsProvider(role_arn="arn:aws:iam::123456789012:role/role_name", session_duration=3600, external_id="external_id")
- Use custom role session name:
- aws = AwsProvider(role_arn="arn:aws:iam::123456789012:role/role_name", role_session_name="custom_session_name")
* Note: You can use the combination of MFA with Assume Role.
- aws = AwsProvider(role_arn="arn:aws:iam::123456789012:role/role_name", mfa=True)
"""
logger.info("Initializing AWS provider ...")
@@ -367,7 +419,7 @@ class AwsProvider(Provider):
"""
get_organizations_info returns a AWSOrganizationsInfo object if the account to be audited is a delegated administrator for AWS Organizations or if the AWS Organizations Role ARN (--organizations-role) is passed.
Arguments:
Args:
- organizations_session: needs to be a Session object with permissions to do organizations:DescribeAccount and organizations:ListTagsForResource.
- aws_account_id: is the AWS Account ID from which we want to get the AWS Organizations account metadata
@@ -424,6 +476,21 @@ class AwsProvider(Provider):
regions: set,
profile_region: str,
) -> AWSIdentityInfo:
"""
set_identity sets the AWS provider identity information.
Args:
- caller_identity: The AWS caller identity information.
- profile: The AWS CLI profile name.
- regions: A set of regions to audit.
- profile_region: The AWS CLI profile region.
Returns:
- AWSIdentityInfo: The AWS provider identity information.
Raises:
- AWSInvalidProviderIdError: If the AWS provider ID is invalid.
"""
logger.info(f"Original AWS Caller Identity UserId: {caller_identity.user_id}")
logger.info(f"Original AWS Caller Identity ARN: {caller_identity.arn}")
@@ -447,6 +514,22 @@ class AwsProvider(Provider):
aws_secret_access_key: str = None,
aws_session_token: Optional[str] = None,
) -> Session:
"""
setup_session sets up an AWS session using the provided credentials.
Args:
- mfa: A boolean indicating whether MFA is enabled.
- profile: The name of the AWS CLI profile to use.
- aws_access_key_id: The AWS access key ID.
- aws_secret_access_key: The AWS secret access key.
- aws_session_token: The AWS session token, optional.
Returns:
- Session: The AWS session.
Raises:
- AWSSetUpSessionError: If an error occurs during the setup process.
"""
try:
logger.debug("Creating original session ...")
@@ -588,6 +671,21 @@ class AwsProvider(Provider):
return refreshed_credentials
def print_credentials(self):
"""
Print the AWS credentials.
This method prints the AWS credentials used by the provider.
Example output:
```
Using the AWS credentials below:
AWS-CLI Profile: default
AWS Regions: all
AWS Account: 123456789012
User Id: AIDAJDPLRKLG7EXAMPLE
Caller Identity ARN: arn:aws:iam::123456789012:user/prowler
```
"""
# Beautify audited regions, set "all" if there is no filter region
regions = (
", ".join(self._identity.audited_regions)
@@ -659,7 +757,7 @@ class AwsProvider(Provider):
"""
get_available_aws_service_regions returns the available regions for the given service and partition.
Arguments:
Args:
- service: The AWS service name.
- partition: The AWS partition name. Default is "aws".
- audited_regions: A set of regions to audit. Default is None.
@@ -679,6 +777,12 @@ class AwsProvider(Provider):
def get_checks_from_input_arn(self) -> set:
"""
get_checks_from_input_arn gets the list of checks from the input arns
Returns:
- set: set of strings representing the checks from the input arns
Example:
checks = get_checks_from_input_arn()
"""
checks_from_arn = set()
is_subservice_in_checks = False
@@ -745,7 +849,18 @@ class AwsProvider(Provider):
# TODO: This can be moved to another class since it doesn't need self
def get_regions_from_audit_resources(self, audit_resources: list) -> set:
"""get_regions_from_audit_resources gets the regions from the audit resources arns"""
"""get_regions_from_audit_resources gets the regions from the audit resources arns
Args:
- audit_resources: list of ARNs of the resources to audit
Returns:
- set: set of strings representing the regions from the audit resources arns
Example:
audit_resources = ["arn:aws:ec2:us-east-1:123456789012:instance/i-1234567890abcdef0"]
regions = get_regions_from_audit_resources(audit_resources)
"""
audited_regions = set()
for resource in audit_resources:
region = resource.split(":")[3]
@@ -805,7 +920,18 @@ class AwsProvider(Provider):
raise error
def get_default_region(self, service: str) -> str:
"""get_default_region returns the default region based on the profile and audited service regions"""
"""get_default_region returns the default region based on the profile and audited service regions
Args:
- service: The AWS service name
Returns:
- str: The default region for the given service
Example:
service = "ec2"
default_region = get_default_region(service)
"""
try:
service_regions = AwsProvider.get_available_aws_service_regions(
service, self._identity.partition, self._identity.audited_regions
@@ -826,7 +952,14 @@ class AwsProvider(Provider):
raise error
def get_global_region(self) -> str:
"""get_global_region returns the global region based on the audited partition"""
"""get_global_region returns the global region based on the audited partition
Returns:
- str: The global region for the audited partition
Example:
global_region = get_global_region()a
"""
global_region = "us-east-1"
if self._identity.partition == "aws-cn":
global_region = "cn-north-1"
@@ -838,7 +971,14 @@ class AwsProvider(Provider):
@staticmethod
def input_role_mfa_token_and_code() -> AWSMFAInfo:
"""input_role_mfa_token_and_code ask for the AWS MFA ARN and TOTP and returns it."""
"""input_role_mfa_token_and_code ask for the AWS MFA ARN and TOTP and returns it.
Returns:
- AWSMFAInfo: An object containing the MFA ARN and TOTP code
Example:
mfa_info = input_role_mfa_token_and_code()
"""
mfa_ARN = input("Enter ARN of MFA: ")
mfa_TOTP = input("Enter MFA code: ")
return AWSMFAInfo(arn=mfa_ARN, totp=mfa_TOTP)
@@ -846,6 +986,12 @@ class AwsProvider(Provider):
def set_session_config(self, retries_max_attempts: int) -> Config:
"""
set_session_config returns a botocore Config object with the Prowler user agent and the default retrier configuration if nothing is passed as argument
Args:
- retries_max_attempts: The maximum number of retries for the standard retrier config
Returns:
- Config: The botocore Config object
"""
# Set the maximum retries for the standard retrier config
default_session_config = Config(
@@ -872,6 +1018,13 @@ class AwsProvider(Provider):
) -> AWSCredentials:
"""
assume_role assumes the IAM roles passed with the given session and returns AWSCredentials
Args:
- session: The AWS session object
- assumed_role_info: The AWSAssumeRoleInfo object
Returns:
- AWSCredentials: The AWS credentials for the assumed role
"""
try:
role_session_name = (
@@ -923,7 +1076,14 @@ class AwsProvider(Provider):
)
def get_aws_enabled_regions(self, current_session: Session) -> set:
"""get_aws_enabled_regions returns a set of enabled AWS regions"""
"""get_aws_enabled_regions returns a set of enabled AWS regions
Args:
- current_session: The AWS session object
Returns:
- set: set of strings representing the enabled AWS regions
"""
try:
# EC2 Client to check enabled regions
service = "ec2"
@@ -947,6 +1107,12 @@ class AwsProvider(Provider):
# TODO: review this function
# Maybe this should be done within the AwsProvider and not in __main__.py
def get_checks_to_execute_by_audit_resources(self) -> set[str]:
"""
get_checks_to_execute_by_audit_resources gets the checks to execute based on the audit resources
Returns:
- set: set of strings representing the checks to execute
"""
# Once the provider is set and we have the eventual checks from arn, it is time to exclude the others
try:
checks = set()
@@ -1273,7 +1439,7 @@ class AwsProvider(Provider):
"""
Create an STS session client.
Parameters:
Args:
- session (session.Session): The AWS session object.
- aws_region (str): The AWS region to use for the session.
@@ -1297,6 +1463,59 @@ class AwsProvider(Provider):
)
raise error
@staticmethod
def get_regions(partition: Partition = Partition.aws) -> set:
"""
Get the available AWS regions from the AWS services JSON file with the ability of filtering by partition.
Args:
partition (str): The AWS partition to retrieve regions for. Defaults to "aws".
Returns:
set: A set of region names.
Raises:
AWSInvalidPartitionError: If the provided partition name is invalid.
Example:
>>> AwsProvider.get_regions("aws")
{"af-south-1"}
"""
try:
regions = set()
data = read_aws_regions_file()
if partition is None:
for service in data["services"].values():
for partition in service["regions"]:
regions.update(service["regions"][partition])
else:
partition = Partition(partition)
for service in data["services"].values():
regions.update(service["regions"][partition.value])
return regions
except ValueError as value_error:
logger.error(
f"{value_error.__class__.__name__}[{value_error.__traceback__.tb_lineno}]: {value_error}"
)
raise AWSInvalidPartitionError(
message=f"Invalid partition: {partition}",
file=os.path.basename(__file__),
)
except KeyError as key_error:
logger.error(
f"{key_error.__class__.__name__}[{key_error.__traceback__.tb_lineno}]: {key_error}"
)
raise AWSInvalidPartitionError(
message=f"Invalid partition: {partition}",
file=os.path.basename(__file__),
)
except Exception as error:
logger.error(f"{error.__class__.__name__}: {error}")
raise error
def read_aws_regions_file() -> dict:
"""
@@ -1313,29 +1532,21 @@ def read_aws_regions_file() -> dict:
return data
def get_aws_available_regions() -> set:
"""
Get the available AWS regions from the AWS services JSON file.
Returns:
set: A set of available AWS regions.
"""
try:
data = read_aws_regions_file()
regions = set()
for service in data["services"].values():
for partition in service["regions"]:
for item in service["regions"][partition]:
regions.add(item)
return regions
except Exception as error:
logger.error(f"{error.__class__.__name__}: {error}")
return set()
# TODO: This can be moved to another class since it doesn't need self
def get_aws_region_for_sts(session_region: str, regions: set[str]) -> str:
"""
Get the AWS region for the STS Assume Role operation.
Args:
- session_region (str): The region configured in the AWS session.
- regions (set[str]): The regions passed with the -f/--region/--filter-region option.
Returns:
str: The AWS region for the STS Assume Role operation
Example:
aws_region = get_aws_region_for_sts(session_region, regions)
"""
# If there is no region passed with -f/--region/--filter-region
if regions is None or len(regions) == 0:
# If you have a region configured in your AWS config or credentials file
@@ -1270,6 +1270,7 @@
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-west-1",
"eu-west-2",
"eu-west-3",
@@ -1280,6 +1281,7 @@
],
"aws-cn": [],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
@@ -1294,6 +1296,7 @@
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-west-1",
"eu-west-2",
"eu-west-3",
@@ -1304,6 +1307,7 @@
],
"aws-cn": [],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
@@ -1318,6 +1322,7 @@
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-west-1",
"eu-west-2",
"eu-west-3",
@@ -1328,6 +1333,7 @@
],
"aws-cn": [],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
@@ -2621,6 +2627,7 @@
"connectcampaigns": {
"regions": {
"aws": [
"af-south-1",
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
@@ -3288,6 +3295,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -3972,6 +3980,7 @@
"ap-northeast-2",
"ap-northeast-3",
"ap-south-1",
"ap-south-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-3",
@@ -3984,6 +3993,7 @@
"eu-west-1",
"eu-west-2",
"eu-west-3",
"il-central-1",
"me-central-1",
"me-south-1",
"sa-east-1",
@@ -4390,6 +4400,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -4831,6 +4842,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -5541,6 +5553,7 @@
"iotfleetwise": {
"regions": {
"aws": [
"ap-south-1",
"eu-central-1",
"us-east-1"
],
@@ -5847,6 +5860,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -5963,6 +5977,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -6235,6 +6250,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -7152,6 +7168,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -7615,15 +7632,9 @@
"opsworkscm": {
"regions": {
"aws": [
"ap-northeast-1",
"ap-southeast-1",
"ap-southeast-2",
"eu-central-1",
"eu-west-1",
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2"
"us-east-1"
],
"aws-cn": [],
"aws-us-gov": []
@@ -9201,6 +9212,7 @@
"ap-southeast-3",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
"eu-south-1",
"eu-south-2",
@@ -9255,7 +9267,6 @@
"eu-west-2",
"eu-west-3",
"il-central-1",
"me-central-1",
"me-south-1",
"sa-east-1",
"us-east-1",
@@ -9263,10 +9274,7 @@
"us-west-1",
"us-west-2"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-cn": [],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
@@ -9848,6 +9856,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -10646,6 +10655,7 @@
"regions": {
"aws": [
"ap-northeast-1",
"ap-south-1",
"ap-southeast-2",
"eu-central-1",
"eu-west-1",
@@ -10680,7 +10690,10 @@
"us-east-2",
"us-west-2"
],
"aws-cn": [],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": []
}
},
@@ -10688,6 +10701,7 @@
"regions": {
"aws": [
"ap-northeast-1",
"ap-south-1",
"ap-southeast-2",
"eu-central-1",
"eu-west-1",
@@ -10766,6 +10780,7 @@
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ap-southeast-5",
"ca-central-1",
"ca-west-1",
"eu-central-1",
@@ -11082,15 +11097,19 @@
"ap-northeast-2",
"ap-northeast-3",
"ap-south-1",
"ap-south-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-3",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
"eu-south-1",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"me-central-1",
"me-south-1",
"sa-east-1",
"us-east-1",
@@ -11337,6 +11356,7 @@
"ap-northeast-1",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-5",
"ca-central-1",
"eu-central-1",
"eu-central-2",
@@ -74,6 +74,10 @@ class AWSBaseException(ProwlerException):
"message": "The provided AWS Session Token is expired",
"remediation": "Get a new AWS Session Token and configure it for the provider.",
},
(1917, "AWSInvalidPartitionError"): {
"message": "The provided AWS partition is invalid",
"remediation": "Check the provided AWS partition and ensure it is valid.",
},
}
def __init__(self, code, file=None, original_exception=None, message=None):
@@ -220,3 +224,10 @@ class AWSSessionTokenExpiredError(AWSCredentialsError):
super().__init__(
1016, file=file, original_exception=original_exception, message=message
)
class AWSInvalidPartitionError(AWSBaseException):
def __init__(self, file=None, original_exception=None, message=None):
super().__init__(
1917, file=file, original_exception=original_exception, message=message
)
@@ -1,7 +1,7 @@
from argparse import ArgumentTypeError, Namespace
from re import fullmatch, search
from prowler.providers.aws.aws_provider import get_aws_available_regions
from prowler.providers.aws.aws_provider import AwsProvider
from prowler.providers.aws.config import ROLE_SESSION_NAME
from prowler.providers.aws.lib.arn.arn import arn_type
@@ -64,7 +64,7 @@ def init_parser(self):
"-f",
nargs="+",
help="AWS region names to run Prowler against",
choices=get_aws_available_regions(),
choices=AwsProvider.get_regions(partition=None),
)
# AWS Organizations
aws_orgs_subparser = aws_parser.add_argument_group("AWS Organizations")
@@ -101,3 +101,17 @@ class AWSService:
except Exception:
# Handle exceptions if necessary
pass # Replace 'pass' with any additional exception handling logic. Currently handled within the called function
def get_unknown_arn(self, resource_type: str = None, region: str = None) -> str:
"""
Generate an unknown ARN for the service
Args:
region (str): The region to get the unknown ARN for.
resource_type (str): The resource type to get the unknown ARN for
Returns:
str: The unknown ARN for the region.
Examples:
>>> service.get_unknown_arn(resource_type="bucket", region="us-east-1")
arn:aws:s3:us-east-1:123456789012:bucket/unknown
"""
return f"arn:{self.audited_partition}:{self.service}:{f'{region}' if region else ''}:{self.audited_account}:{f'{resource_type}/' if resource_type else ''}unknown"
+24
View File
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from boto3.session import Session
from botocore.config import Config
@@ -82,6 +83,29 @@ class AWSMFAInfo:
totp: str
class Partition(str, Enum):
"""
Enum class representing different AWS partitions.
Attributes:
aws (str): Represents the standard AWS commercial regions.
aws_cn (str): Represents the AWS China regions.
aws_us_gov (str): Represents the AWS GovCloud (US) Regions.
aws_iso (str): Represents the AWS ISO (US) Regions.
aws_iso_b (str): Represents the AWS ISOB (US) Regions.
aws_iso_e (str): Represents the AWS ISOE (Europe) Regions.
aws_iso_f (str): Represents the AWS ISOF Regions.
"""
aws = "aws"
aws_cn = "aws-cn"
aws_us_gov = "aws-us-gov"
aws_iso = "aws-iso"
aws_iso_b = "aws-iso-b"
aws_iso_e = "aws-iso-e"
aws_iso_f = "aws-iso-f"
class AWSOutputOptions(ProviderOutputOptions):
security_hub_enabled: bool
@@ -22,7 +22,7 @@ class accessanalyzer_enabled(Check):
else:
if analyzer.status == "NOT_AVAILABLE":
report.status = "FAIL"
report.status_extended = f"IAM Access Analyzer in account {analyzer.name} is not enabled."
report.status_extended = f"IAM Access Analyzer in account {accessanalyzer_client.audited_account} is not enabled."
else:
report.status = "FAIL"
@@ -6,7 +6,8 @@ from prowler.providers.aws.services.accessanalyzer.accessanalyzer_client import
def fixer(region):
"""
Enable Access Analyzer in a region. Requires the access-analyzer:CreateAnalyzer permission:
Enable Access Analyzer in a region. Requires the access-analyzer:CreateAnalyzer permission.
Permissions:
{
"Version": "2012-10-17",
"Statement": [
@@ -43,8 +43,10 @@ class AccessAnalyzer(AWSService):
if analyzer_count == 0:
self.analyzers.append(
Analyzer(
arn=self.audited_account_arn,
name=self.audited_account,
arn=self.get_unknown_arn(
region=regional_client.region, resource_type="analyzer"
),
name="analyzer/unknown",
status="NOT_AVAILABLE",
tags=[],
type="",
@@ -25,7 +25,9 @@
"Url": "https://aws.amazon.com/blogs/security/how-to-get-ready-for-certificate-transparency/"
}
},
"Categories": [],
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
@@ -29,7 +29,8 @@
}
},
"Categories": [
"forensics-ready"
"forensics-ready",
"logging"
],
"DependsOn": [],
"RelatedTo": [],
@@ -28,7 +28,9 @@
"Url": "https://docs.aws.amazon.com/apigateway/latest/developerguide/security-monitoring.html"
}
},
"Categories": [],
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
@@ -0,0 +1,4 @@
from prowler.providers.aws.services.appsync.appsync_service import AppSync
from prowler.providers.common.provider import Provider
appsync_client = AppSync(Provider.get_global_provider())
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "appsync_field_level_logging_enabled",
"CheckTitle": "AWS AppSync should have field-level logging enabled",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "appsync",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:appsync:{region}:{account-id}:apis/{api-id}",
"Severity": "medium",
"ResourceType": "AwsAppSyncGraphQLApi",
"Description": "This control checks whether an AWS AppSync API (only GraphQL APIs since boto3 doesnt have a method to return other APIs) field-level logging turned on. The control fails if the field resolver log level is set to None.",
"Risk": "Without field-level logging enabled, it's difficult to monitor, troubleshoot, and optimize GraphQL API queries effectively.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/appsync-logging-enabled.html",
"Remediation": {
"Code": {
"CLI": "aws appsync update-graphql-api --api-id <api-id> --log-config fieldLogLevel=<fieldLoggingLevel>",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/appsync-controls.html#appsync-2",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable field-level logging for your AWS AppSync API to monitor and troubleshoot GraphQL queries effectively.",
"Url": "https://docs.aws.amazon.com/appsync/latest/devguide/monitoring.html#setup-and-configuration"
}
},
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,26 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.appsync.appsync_client import appsync_client
class appsync_field_level_logging_enabled(Check):
def execute(self):
findings = []
# Check only GraphQL APIs because boto3 does not have a method to get other types of AppSync APIs (list_apis is not working)
for api in appsync_client.graphql_apis.values():
report = Check_Report_AWS(self.metadata())
report.region = api.region
report.resource_id = api.id
report.resource_arn = api.arn
report.resource_tags = api.tags
report.status = "PASS"
report.status_extended = (
f"AppSync API {api.name} has field log level enabled."
)
if api.field_log_level != "ALL" and api.field_log_level != "ERROR":
report.status = "FAIL"
report.status_extended = (
f"AppSync API {api.name} does not have field log level enabled."
)
findings.append(report)
return findings
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "appsync_graphql_api_no_api_key_authentication",
"CheckTitle": "AWS AppSync GraphQL APIs should not be authenticated with API keys",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "appsync",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:appsync:{region}:{account-id}:apis/{api-id}",
"Severity": "high",
"ResourceType": "AwsAppSyncGraphQLApi",
"Description": "This control checks whether your application uses an API key to interact with an AWS AppSync GraphQL API. The control fails if an AWS AppSync GraphQL API is authenticated with an API key.",
"Risk": "API keys in AppSync can expose applications to unauthorized access if compromised. Avoiding API keys helps reduce the risk of unintended access.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/appsync-authorization-check.html",
"Remediation": {
"Code": {
"CLI": "aws appsync update-graphql-api --api-id <api-id> --authentication-type <authentication-type>",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/appsync-controls.html#appsync-5",
"Terraform": ""
},
"Recommendation": {
"Text": "Use authentication methods other than API keys for AWS AppSync GraphQL APIs, such as AWS_IAM or Amazon Cognito.",
"Url": "https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html"
}
},
"Categories": [
"trustboundaries"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,22 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.appsync.appsync_client import appsync_client
class appsync_graphql_api_no_api_key_authentication(Check):
def execute(self):
findings = []
for api in appsync_client.graphql_apis.values():
if api.type == "GRAPHQL":
report = Check_Report_AWS(self.metadata())
report.region = api.region
report.resource_id = api.id
report.resource_arn = api.arn
report.resource_tags = api.tags
report.status = "PASS"
report.status_extended = f"AppSync GraphQL API {api.name} is not using an API KEY for authentication."
if api.authentication_type == "API_KEY":
report.status = "FAIL"
report.status_extended = f"AppSync GraphQL API {api.name} is using an API KEY for authentication."
findings.append(report)
return findings
@@ -0,0 +1,61 @@
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 AppSync(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.graphql_apis = {}
self.__threading_call__(self._list_graphql_apis)
def _list_graphql_apis(self, regional_client):
logger.info("AppSync - Describing APIs...")
try:
list_graphql_apis_paginator = regional_client.get_paginator(
"list_graphql_apis"
)
for page in list_graphql_apis_paginator.paginate():
for api in page["graphqlApis"]:
api_arn = api["arn"]
if not self.audit_resources or (
is_resource_filtered(
api_arn,
self.audit_resources,
)
):
self.graphql_apis[api_arn] = GraphqlApi(
id=api["apiId"],
name=api["name"],
arn=api_arn,
region=regional_client.region,
type=api.get("apiType", "GRAPHQL"),
field_log_level=api.get("logConfig", {}).get(
"fieldLogLevel", ""
),
authentication_type=api.get(
"authenticationType", "API_KEY"
),
tags=[api.get("tags", {})],
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class GraphqlApi(BaseModel):
id: str
name: str
arn: str
region: str
type: str
field_log_level: str
authentication_type: str
tags: Optional[list] = []
@@ -24,7 +24,8 @@
}
},
"Categories": [
"forensics-ready"
"forensics-ready",
"logging"
],
"DependsOn": [],
"RelatedTo": [],
@@ -183,21 +183,26 @@ class Backup(AWSService):
def _list_recovery_points(self, regional_client):
logger.info("Backup - Listing Recovery Points...")
try:
for backup_vault in self.backup_vaults:
paginator = regional_client.get_paginator(
"list_recovery_points_by_backup_vault"
)
for page in paginator.paginate(BackupVaultName=backup_vault.name):
for recovery_point in page.get("RecoveryPoints", []):
self.recovery_points.append(
RecoveryPoint(
arn=recovery_point.get("RecoveryPointArn"),
backup_vault_name=backup_vault.name,
encrypted=recovery_point.get("IsEncrypted", False),
backup_vault_region=backup_vault.region,
tags=[],
)
)
if self.backup_vaults:
for backup_vault in self.backup_vaults:
paginator = regional_client.get_paginator(
"list_recovery_points_by_backup_vault"
)
for page in paginator.paginate(BackupVaultName=backup_vault.name):
for recovery_point in page.get("RecoveryPoints", []):
arn = recovery_point.get("RecoveryPointArn")
if arn:
self.recovery_points.append(
RecoveryPoint(
arn=arn,
backup_vault_name=backup_vault.name,
encrypted=recovery_point.get(
"IsEncrypted", False
),
backup_vault_region=backup_vault.region,
tags=[],
)
)
except ClientError as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -8,8 +8,10 @@ class bedrock_model_invocation_logging_enabled(Check):
for region, logging in bedrock_client.logging_configurations.items():
report = Check_Report_AWS(self.metadata())
report.region = region
report.resource_id = bedrock_client.audited_account
report.resource_arn = bedrock_client.audited_account_arn
report.resource_id = "model-invocation-logging"
report.resource_arn = (
bedrock_client._get_model_invocation_logging_arn_template(region)
)
report.status = "FAIL"
report.status_extended = "Bedrock Model Invocation Logging is disabled."
if logging.enabled:
@@ -13,8 +13,10 @@ class bedrock_model_invocation_logs_encryption_enabled(Check):
cloudwatch_encryption = True
report = Check_Report_AWS(self.metadata())
report.region = region
report.resource_id = bedrock_client.audited_account
report.resource_arn = bedrock_client.audited_account_arn
report.resource_id = "model-invocation-logging"
report.resource_arn = (
bedrock_client._get_model_invocation_logging_arn_template(region)
)
report.status = "PASS"
report.status_extended = "Bedrock Model Invocation logs are encrypted."
if logging.s3_bucket:
@@ -18,6 +18,13 @@ class Bedrock(AWSService):
self.__threading_call__(self._get_guardrail, self.guardrails.values())
self.__threading_call__(self._list_tags_for_resource, self.guardrails.values())
def _get_model_invocation_logging_arn_template(self, region):
return (
f"arn:{self.audited_partition}:bedrock:{region}:{self.audited_account}:model-invocation-logging"
if region
else f"arn:{self.audited_partition}:bedrock:{self.region}:{self.audited_account}:model-invocation-logging"
)
def _get_model_invocation_logging_configuration(self, regional_client):
logger.info("Bedrock - Getting Model Invocation Logging Configuration...")
try:
@@ -24,7 +24,8 @@
}
},
"Categories": [
"forensics-ready"
"forensics-ready",
"logging"
],
"DependsOn": [],
"RelatedTo": [],
@@ -28,7 +28,8 @@
}
},
"Categories": [
"forensics-ready"
"forensics-ready",
"logging"
],
"DependsOn": [],
"RelatedTo": [],
@@ -7,7 +7,8 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import (
def fixer(region):
"""
NOTE: Define the S3 bucket name in the fixer_config.yaml file.
Enable CloudTrail in a region. Requires the cloudtrail:CreateTrail permission:
Enable CloudTrail in a region. Requires the cloudtrail:CreateTrail permission.
Permissions:
{
"Version": "2012-10-17",
"Statement": [
@@ -67,10 +67,8 @@ class cloudtrail_threat_detection_enumeration(Check):
found_potential_enumeration = True
report = Check_Report_AWS(self.metadata())
report.region = cloudtrail_client.region
report.resource_id = cloudtrail_client.audited_account
report.resource_arn = cloudtrail_client._get_trail_arn_template(
cloudtrail_client.region
)
report.resource_id = aws_identity_arn.split("/")[-1]
report.resource_arn = aws_identity_arn
report.status = "FAIL"
report.status_extended = f"Potential enumeration attack detected from AWS {aws_identity_type} {aws_identity_arn.split('/')[-1]} with an threshold of {identity_threshold}."
findings.append(report)
@@ -67,10 +67,8 @@ class cloudtrail_threat_detection_llm_jacking(Check):
found_potential_llm_jacking = True
report = Check_Report_AWS(self.metadata())
report.region = cloudtrail_client.region
report.resource_id = cloudtrail_client.audited_account
report.resource_arn = cloudtrail_client._get_trail_arn_template(
cloudtrail_client.region
)
report.resource_id = aws_identity_arn.split("/")[-1]
report.resource_arn = aws_identity_arn
report.status = "FAIL"
report.status_extended = f"Potential LLM Jacking attack detected from AWS {aws_identity_type} {aws_identity_arn.split('/')[-1]} with an threshold of {identity_threshold}."
findings.append(report)
@@ -69,10 +69,8 @@ class cloudtrail_threat_detection_privilege_escalation(Check):
found_potential_privilege_escalation = True
report = Check_Report_AWS(self.metadata())
report.region = cloudtrail_client.region
report.resource_id = cloudtrail_client.audited_account
report.resource_arn = cloudtrail_client._get_trail_arn_template(
cloudtrail_client.region
)
report.resource_id = aws_identity_arn.split("/")[-1]
report.resource_arn = aws_identity_arn
report.status = "FAIL"
report.status_extended = f"Potential privilege escalation attack detected from AWS {aws_identity_type} {aws_identity_arn.split('/')[-1]} with an threshold of {identity_threshold}."
findings.append(report)
@@ -20,8 +20,10 @@ class directconnect_connection_redundancy(Check):
for region, connections in regions.items():
report = Check_Report_AWS(self.metadata())
report.region = region
report.resource_arn = directconnect_client.audited_account_arn
report.resource_id = directconnect_client.audited_account
report.resource_arn = directconnect_client._get_connection_arn_template(
region
)
report.resource_id = "unknown"
if connections["Connections"] == 1:
report.status = "FAIL"
report.status_extended = (
@@ -18,6 +18,13 @@ class DirectConnect(AWSService):
self.__threading_call__(self._describe_connections)
self.__threading_call__(self._describe_vifs)
def _get_connection_arn_template(self, region):
return (
f"arn:{self.audited_partition}:directconnect:{region}:{self.audited_account}:dxcon"
if region
else f"arn:{self.audited_partition}:directconnect:{self.region}:{self.audited_account}:dxcon"
)
def _describe_connections(self, regional_client):
"""List DirectConnect(s) in the given region.
@@ -23,7 +23,9 @@
"Url": "https://docs.aws.amazon.com/directoryservice/latest/admin-guide/incident-response.html"
}
},
"Categories": [],
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "dms_endpoint_redis_in_transit_encryption_enabled",
"CheckTitle": "Check if DMS endpoints for Redis OSS are encrypted in transit.",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "dms",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:dms:region:account-id:endpoint/endpoint-id",
"Severity": "medium",
"ResourceType": "AwsDmsEndpoint",
"Description": "This control checks whether an AWS DMS endpoint for Redis OSS is configured with a TLS connection. The control fails if the endpoint doesn't have TLS enabled.",
"Risk": "Without TLS, data transmitted between databases may be vulnerable to interception or eavesdropping, increasing the risk of data breaches and other security incidents.",
"RelatedUrl": "https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.Redis.html",
"Remediation": {
"Code": {
"CLI": "aws dms modify-endpoint --endpoint-arn <endpoint-arn> --redis-settings '{'SslSecurityProtocol': 'ssl-encryption'}'",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/dms-controls.html#dms-12",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable TLS for DMS endpoints for Redis OSS to ensure encrypted communication during data migration.",
"Url": "https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.Redis.html#CHAP_Target.Redis.EndpointSettings"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,42 @@
from typing import List
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.dms.dms_client import dms_client
class dms_endpoint_redis_in_transit_encryption_enabled(Check):
"""
Check if AWS DMS Endpoints for Redis OSS have TLS enabled.
This class verifies whether each AWS DMS Endpoint configured for Redis OSS is encrypted in transit
by checking the `TlsEnabled` property in the endpoint's configuration. The check ensures that
TLS is enabled to secure data in transit, preventing unauthorized access and ensuring data integrity.
"""
def execute(self) -> List[Check_Report_AWS]:
"""
Execute the DMS Redis TLS enabled check.
Iterates over all DMS Endpoints and generates a report indicating whether
each Redis OSS endpoint is encrypted in transit.
Returns:
List[Check_Report_AWS]: A list of report objects with the results of the check.
"""
findings = []
for endpoint_arn, endpoint in dms_client.endpoints.items():
if endpoint.engine_name == "redis":
report = Check_Report_AWS(self.metadata())
report.resource_id = endpoint.id
report.resource_arn = endpoint_arn
report.region = endpoint.region
report.resource_tags = endpoint.tags
report.status = "FAIL"
report.status_extended = f"DMS Endpoint {endpoint.id} for Redis OSS is not encrypted in transit."
if endpoint.redis_ssl_protocol == "ssl-encryption":
report.status = "PASS"
report.status_extended = f"DMS Endpoint {endpoint.id} for Redis OSS is encrypted in transit."
findings.append(report)
return findings
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "dms_replication_task_source_logging_enabled",
"CheckTitle": "Check if DMS replication tasks for the source database have logging enabled.",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "dms",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:dms:region:account-id:task/task-id",
"Severity": "medium",
"ResourceType": "AwsDmsReplicationTask",
"Description": "This control checks whether logging is enabled with the minimum severity level of LOGGER_SEVERITY_DEFAULT for DMS replication tasks SOURCE_CAPTURE and SOURCE_UNLOAD. The control fails if logging isn't enabled for these tasks or if the minimum severity level is less than LOGGER_SEVERITY_DEFAULT.",
"Risk": "Without logging enabled, issues in data migration may go undetected, affecting the integrity and compliance of replicated data.",
"RelatedUrl": "https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Monitoring.html#CHAP_Monitoring.ManagingLogs",
"Remediation": {
"Code": {
"CLI": "aws dms modify-replication-task --replication-task-arn <task-arn> --task-settings '{\"Logging\":{\"EnableLogging\":true,\"LogComponents\":[{\"Id\":\"SOURCE_CAPTURE\",\"Severity\":\"LOGGER_SEVERITY_DEFAULT\"},{\"Id\":\"SOURCE_UNLOAD\",\"Severity\":\"LOGGER_SEVERITY_DEFAULT\"}]}}'",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/dms-controls.html#dms-8",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable logging for source database DMS replication tasks with a minimum severity level of LOGGER_SEVERITY_DEFAULT.",
"Url": "https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Tasks.CustomizingTasks.TaskSettings.Logging.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,79 @@
from typing import List
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.dms.dms_client import dms_client
class dms_replication_task_source_logging_enabled(Check):
"""
Check if AWS DMS replication tasks have logging enabled with the required
logging components and severity levels.
This class verifies that each DMS replication task has logging enabled
and that the components SOURCE_CAPTURE and SOURCE_UNLOAD are configured with
at least LOGGER_SEVERITY_DEFAULT severity level. If either component is missing
or does not meet the minimum severity requirement, the check will fail.
"""
def execute(self) -> List[Check_Report_AWS]:
"""
Execute the DMS replication task logging requirements check.
Iterates over all DMS replication tasks and generates a report indicating
whether each task has logging enabled and meets the logging requirements
for SOURCE_CAPTURE and SOURCE_UNLOAD components.
Returns:
List[Check_Report_AWS]: A list of report objects with the results of the check.
"""
MINIMUM_SEVERITY_LEVELS = [
"LOGGER_SEVERITY_DEFAULT",
"LOGGER_SEVERITY_DEBUG",
"LOGGER_SEVERITY_DETAILED_DEBUG",
]
findings = []
for (
replication_task_arn,
replication_task,
) in dms_client.replication_tasks.items():
report = Check_Report_AWS(self.metadata())
report.resource_id = replication_task.id
report.resource_arn = replication_task_arn
report.region = replication_task.region
report.resource_tags = replication_task.tags
if not replication_task.logging_enabled:
report.status = "FAIL"
report.status_extended = f"DMS Replication Task {replication_task.id} does not have logging enabled for source events."
else:
missing_components = []
source_capture_compliant = False
source_unload_compliant = False
for component in replication_task.log_components:
if (
component["Id"] == "SOURCE_CAPTURE"
and component["Severity"] in MINIMUM_SEVERITY_LEVELS
):
source_capture_compliant = True
elif (
component["Id"] == "SOURCE_UNLOAD"
and component["Severity"] in MINIMUM_SEVERITY_LEVELS
):
source_unload_compliant = True
if not source_capture_compliant:
missing_components.append("Source Capture")
if not source_unload_compliant:
missing_components.append("Source Unload")
if source_capture_compliant and source_unload_compliant:
report.status = "PASS"
report.status_extended = f"DMS Replication Task {replication_task.id} has logging enabled with the minimum severity level in source events."
else:
report.status = "FAIL"
report.status_extended = f"DMS Replication Task {replication_task.id} does not meet the minimum severity level of logging in {' and '.join(missing_components)} events."
findings.append(report)
return findings
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "dms_replication_task_target_logging_enabled",
"CheckTitle": "Check if DMS replication tasks for the target database have logging enabled.",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "dms",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:dms:region:account-id:task/task-id",
"Severity": "medium",
"ResourceType": "AwsDmsReplicationTask",
"Description": "This control checks whether logging is enabled with the minimum severity level of LOGGER_SEVERITY_DEFAULT for DMS replication tasks TARGET_APPLY and TARGET_LOAD. The control fails if logging isn't enabled for these tasks or if the minimum severity level is less than LOGGER_SEVERITY_DEFAULT.",
"Risk": "Without logging enabled, issues in data migration may go undetected, affecting the integrity and compliance of replicated data.",
"RelatedUrl": "https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Monitoring.html#CHAP_Monitoring.ManagingLogs",
"Remediation": {
"Code": {
"CLI": "aws dms modify-replication-task --replication-task-arn <task-arn> --task-settings '{\"Logging\":{\"EnableLogging\":true,\"LogComponents\":[{\"Id\":\"TARGET_APPLY\",\"Severity\":\"LOGGER_SEVERITY_DEFAULT\"},{\"Id\":\"TARGET_LOAD\",\"Severity\":\"LOGGER_SEVERITY_DEFAULT\"}]}}'",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/dms-controls.html#dms-7",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable logging for target database DMS replication tasks with a minimum severity level of LOGGER_SEVERITY_DEFAULT.",
"Url": "https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Tasks.CustomizingTasks.TaskSettings.Logging.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,79 @@
from typing import List
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.dms.dms_client import dms_client
class dms_replication_task_target_logging_enabled(Check):
"""
Check if AWS DMS replication tasks have logging enabled with the required
logging components and severity levels.
This class verifies that each DMS replication task has logging enabled
and that the components TARGET_APPLY and TARGET_LOAD are configured with
at least LOGGER_SEVERITY_DEFAULT severity level. If either component is missing
or does not meet the minimum severity requirement, the check will fail.
"""
def execute(self) -> List[Check_Report_AWS]:
"""
Execute the DMS replication task logging requirements check.
Iterates over all DMS replication tasks and generates a report indicating
whether each task has logging enabled and meets the logging requirements
for TARGET_APPLY and TARGET_LOAD components.
Returns:
List[Check_Report_AWS]: A list of report objects with the results of the check.
"""
MINIMUM_SEVERITY_LEVELS = [
"LOGGER_SEVERITY_DEFAULT",
"LOGGER_SEVERITY_DEBUG",
"LOGGER_SEVERITY_DETAILED_DEBUG",
]
findings = []
for (
replication_task_arn,
replication_task,
) in dms_client.replication_tasks.items():
report = Check_Report_AWS(self.metadata())
report.resource_id = replication_task.id
report.resource_arn = replication_task_arn
report.region = replication_task.region
report.resource_tags = replication_task.tags
if not replication_task.logging_enabled:
report.status = "FAIL"
report.status_extended = f"DMS Replication Task {replication_task.id} does not have logging enabled for target events."
else:
missing_components = []
source_capture_compliant = False
source_unload_compliant = False
for component in replication_task.log_components:
if (
component["Id"] == "TARGET_APPLY"
and component["Severity"] in MINIMUM_SEVERITY_LEVELS
):
source_capture_compliant = True
elif (
component["Id"] == "TARGET_LOAD"
and component["Severity"] in MINIMUM_SEVERITY_LEVELS
):
source_unload_compliant = True
if not source_capture_compliant:
missing_components.append("Target Apply")
if not source_unload_compliant:
missing_components.append("Target Load")
if source_capture_compliant and source_unload_compliant:
report.status = "PASS"
report.status_extended = f"DMS Replication Task {replication_task.id} has logging enabled with the minimum severity level in target events."
else:
report.status = "FAIL"
report.status_extended = f"DMS Replication Task {replication_task.id} does not meet the minimum severity level of logging in {' and '.join(missing_components)} events."
findings.append(report)
return findings
@@ -1,3 +1,4 @@
import json
from typing import Optional
from pydantic import BaseModel
@@ -13,10 +14,14 @@ class DMS(AWSService):
super().__init__(__class__.__name__, provider)
self.instances = []
self.endpoints = {}
self.replication_tasks = {}
self.__threading_call__(self._describe_replication_instances)
self.__threading_call__(self._list_tags, self.instances)
self.__threading_call__(self._describe_endpoints)
self.__threading_call__(self._describe_replication_tasks)
self.__threading_call__(self._list_tags, self.endpoints.values())
self.__threading_call__(self._describe_replication_tasks)
self.__threading_call__(self._list_tags, self.replication_tasks.values())
def _describe_replication_instances(self, regional_client):
logger.info("DMS - Describing DMS Replication Instances...")
@@ -71,6 +76,9 @@ class DMS(AWSService):
id=endpoint["EndpointIdentifier"],
region=regional_client.region,
ssl_mode=endpoint.get("SslMode", False),
redis_ssl_protocol=endpoint.get("RedisSettings", {}).get(
"SslSecurityProtocol", "plaintext"
),
mongodb_auth_type=endpoint.get("MongoDbSettings", {}).get(
"AuthType", "no"
),
@@ -84,6 +92,37 @@ class DMS(AWSService):
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _describe_replication_tasks(self, regional_client):
logger.info("DMS - Describing DMS Replication Tasks for Logging Settings...")
try:
paginator = regional_client.get_paginator("describe_replication_tasks")
for page in paginator.paginate():
for task in page["ReplicationTasks"]:
arn = task["ReplicationTaskArn"]
if not self.audit_resources or (
is_resource_filtered(arn, self.audit_resources)
):
task_settings = json.loads(
task.get("ReplicationTaskSettings", "")
)
self.replication_tasks[arn] = ReplicationTasks(
arn=arn,
id=task["ReplicationTaskIdentifier"],
region=regional_client.region,
source_endpoint_arn=task["SourceEndpointArn"],
target_endpoint_arn=task["TargetEndpointArn"],
logging_enabled=task_settings.get("Logging", {}).get(
"EnableLogging", False
),
log_components=task_settings.get("Logging", {}).get(
"LogComponents", []
),
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _list_tags(self, resource: any):
try:
resource.tags = self.regional_clients[
@@ -100,10 +139,11 @@ class Endpoint(BaseModel):
id: str
region: str
ssl_mode: str
tags: Optional[list]
redis_ssl_protocol: str
mongodb_auth_type: str
neptune_iam_auth_enabled: bool = False
engine_name: str
tags: Optional[list]
class RepInstance(BaseModel):
@@ -117,3 +157,14 @@ class RepInstance(BaseModel):
multi_az: bool
region: str
tags: Optional[list] = []
class ReplicationTasks(BaseModel):
arn: str
id: str
region: str
source_endpoint_arn: str
target_endpoint_arn: str
logging_enabled: bool = False
log_components: list[dict] = []
tags: Optional[list] = []
@@ -0,0 +1,42 @@
from prowler.lib.logger import logger
from prowler.providers.aws.services.documentdb.documentdb_client import (
documentdb_client,
)
def fixer(resource_id: str, region: str) -> bool:
"""
Modify the attributes of a DocumentDB cluster snapshot to remove public access.
Specifically, this fixer removes the 'all' value from the 'restore' attribute to
prevent the snapshot from being publicly accessible. Requires the rds:ModifyDBClusterSnapshotAttribute permissions.
Permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "rds:ModifyDBClusterSnapshotAttribute",
"Resource": "*"
}
]
}
Args:
resource_id (str): The DB cluster snapshot identifier.
region (str): AWS region where the snapshot exists.
Returns:
bool: True if the operation is successful (public access is removed), False otherwise.
"""
try:
regional_client = documentdb_client.regional_clients[region]
regional_client.modify_db_cluster_snapshot_attribute(
DBClusterSnapshotIdentifier=resource_id,
AttributeName="restore",
ValuesToRemove=["all"],
)
except Exception as error:
logger.error(
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return False
else:
return True
@@ -5,7 +5,8 @@ from prowler.providers.aws.services.ec2.ec2_client import ec2_client
def fixer(region):
"""
Enable EBS encryption by default in a region. NOTE: Custom KMS keys for EBS Default Encryption may be overwritten.
Requires the ec2:EnableEbsEncryptionByDefault permission:
Requires the ec2:EnableEbsEncryptionByDefault permission.
Permissions:
{
"Version": "2012-10-17",
"Statement": [
@@ -0,0 +1,41 @@
from prowler.lib.logger import logger
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
def fixer(resource_id: str, region: str) -> bool:
"""
Modify the attributes of an EBS snapshot to remove public access.
Specifically, this fixer removes the 'all' value from the 'createVolumePermission' attribute to
prevent the snapshot from being publicly accessible. Requires the ec2:ModifySnapshotAttribute permission.
Permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:ModifySnapshotAttribute",
"Resource": "*"
}
]
}
Args:
resource_id (str): The snapshot identifier.
region (str): AWS region where the snapshot exists.
Returns:
bool: True if the operation is successful (public access is removed), False otherwise.
"""
try:
regional_client = ec2_client.regional_clients[region]
regional_client.modify_snapshot_attribute(
SnapshotId=resource_id,
Attribute="createVolumePermission",
OperationType="remove",
GroupNames=["all"],
)
except Exception as error:
logger.error(
f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
return False
else:
return True
@@ -5,7 +5,8 @@ from prowler.providers.aws.services.ec2.ec2_client import ec2_client
def fixer(region):
"""
Enable EBS snapshot block public access in a region.
Requires the ec2:EnableSnapshotBlockPublicAccess permission:
Requires the ec2:EnableSnapshotBlockPublicAccess permission.
Permissions:
{
"Version": "2012-10-17",
"Statement": [
@@ -8,7 +8,7 @@
"ServiceName": "ec2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id",
"Severity": "medium",
"Severity": "high",
"ResourceType": "AwsEc2Instance",
"Description": "Ensure Instance Metadata Service Version 2 (IMDSv2) is enforced for EC2 instances at the account level to protect against SSRF vulnerabilities.",
"Risk": "EC2 instances that use IMDSv1 are vulnerable to SSRF attacks.",
@@ -5,7 +5,8 @@ from prowler.providers.aws.services.ec2.ec2_client import ec2_client
def fixer(region):
"""
Enable IMDSv2 for EC2 instances in the specified region.
Requires the ec2:ModifyInstanceMetadataDefaults permission:
Requires the ec2:ModifyInstanceMetadataDefaults permission.
Permissions:
{
"Version": "2012-10-17",
"Statement": [
@@ -8,7 +8,7 @@
"ServiceName": "ec2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"Severity": "high",
"ResourceType": "AwsEc2Instance",
"Description": "Check if EC2 Instance Metadata Service Version 2 (IMDSv2) is Enabled and Required.",
"Risk": "Using IMDSv2 will protect from misconfiguration and SSRF vulnerabilities. IMDSv1 will not.",
@@ -26,7 +26,8 @@
}
},
"Categories": [
"forensics-ready"
"forensics-ready",
"logging"
],
"DependsOn": [],
"RelatedTo": [],
@@ -26,7 +26,8 @@
}
},
"Categories": [
"forensics-ready"
"forensics-ready",
"logging"
],
"DependsOn": [],
"RelatedTo": [],
@@ -25,7 +25,10 @@
"Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html"
}
},
"Categories": [],
"Categories": [
"forensics-ready",
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
@@ -0,0 +1,4 @@
from prowler.providers.aws.services.firehose.firehose_service import Firehose
from prowler.providers.common.provider import Provider
firehose_client = Firehose(Provider.get_global_provider())
@@ -0,0 +1,96 @@
from enum import Enum
from typing import Dict, List, Optional
from botocore.client import ClientError
from pydantic import BaseModel, Field
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 Firehose(AWSService):
def __init__(self, provider):
# Call AWSService's __init__
super().__init__(__class__.__name__, provider)
self.delivery_streams = {}
self.__threading_call__(self._list_delivery_streams)
self.__threading_call__(
self._list_tags_for_delivery_stream, self.delivery_streams.values()
)
self.__threading_call__(
self._describe_delivery_stream, self.delivery_streams.values()
)
def _list_delivery_streams(self, regional_client):
logger.info("Firehose - Listing delivery streams...")
try:
for stream_name in regional_client.list_delivery_streams()[
"DeliveryStreamNames"
]:
stream_arn = f"arn:{self.audited_partition}:firehose:{regional_client.region}:{self.audited_account}:deliverystream/{stream_name}"
if not self.audit_resources or (
is_resource_filtered(stream_arn, self.audit_resources)
):
self.delivery_streams[stream_arn] = DeliveryStream(
arn=stream_arn,
name=stream_name,
region=regional_client.region,
)
except ClientError as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _list_tags_for_delivery_stream(self, stream):
logger.info(f"Firehose - Listing tags for stream {stream.name}...")
try:
stream.tags = (
self.regional_clients[stream.region]
.list_tags_for_delivery_stream(DeliveryStreamName=stream.name)
.get("Tags", [])
)
except ClientError as error:
logger.error(
f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def _describe_delivery_stream(self, stream):
logger.info(f"Firehose - Describing stream {stream.name}...")
try:
describe_stream = self.regional_clients[
stream.region
].describe_delivery_stream(DeliveryStreamName=stream.name)
encryption_config = describe_stream.get(
"DeliveryStreamDescription", {}
).get("DeliveryStreamEncryptionConfiguration", {})
stream.kms_encryption = EncryptionStatus(
encryption_config.get("Status", "DISABLED")
)
stream.kms_key_arn = encryption_config.get("KeyARN", "")
except ClientError as error:
logger.error(
f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
class EncryptionStatus(Enum):
"""Possible values for the status of the encryption of a Firehose stream"""
ENABLED = "ENABLED"
DISABLED = "DISABLED"
ENABLING = "ENABLING"
DISABLING = "DISABLING"
ENABLING_FAILED = "ENABLING_FAILED"
DISABLING_FAILED = "DISABLING_FAILED"
class DeliveryStream(BaseModel):
"""Model for a Firehose Delivery Stream"""
arn: str
name: str
region: str
kms_key_arn: Optional[str] = Field(default_factory=str)
kms_encryption: Optional[str] = Field(default_factory=str)
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "firehose_stream_encrypted_at_rest",
"CheckTitle": "DataFirehose delivery streams should be encrypted at rest.",
"CheckType": [
"Software and Configuration Checks/Industry and Regulatory Standards/NIST 800-53 Controls"
],
"ServiceName": "firehose",
"SubServiceName": "Ensure DataFirehose delivery streams are encrypted at rest.",
"ResourceIdTemplate": "arn:partition:firehose:region:account-id:deliverystream/delivery-stream-id",
"Severity": "medium",
"ResourceType": "AwsKinesisFirehoseDeliveryStream",
"Description": "",
"Risk": "Without encryption at rest, data in Amazon Kinesis Data Firehose delivery streams is vulnerable to unauthorized access if the storage layer is compromised. This increases the risk of sensitive information exposure, potentially leading to data breaches or non-compliance with security regulations.",
"RelatedUrl": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html",
"Remediation": {
"Code": {
"CLI": "aws firehose update-delivery-stream --delivery-stream-name <delivery-stream-name> --delivery-stream-encryption-configuration-input '{ \"KeyType\": \"CUSTOMER_MANAGED_CMK\", \"KeyARN\": \"<kms-key-arn>\" }'",
"NativeIaC": "https://docs.prowler.com/checks/aws/general-policies/ensure-aws-kinesis-firehoses-delivery-stream-is-encrypted/",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/datafirehose-controls.html#datafirehose-1",
"Terraform": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Firehose/delivery-stream-encrypted-with-kms-customer-master-keys.html"
},
"Recommendation": {
"Text": "Enable server-side encryption for Kinesis Firehose delivery streams using AWS Key Management Service (KMS). This encrypts data at rest, ensuring that sensitive information remains secure and compliant with regulatory standards.",
"Url": "https://docs.aws.amazon.com/firehose/latest/dev/encryption.html"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
@@ -0,0 +1,40 @@
from typing import List
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.firehose.firehose_client import firehose_client
from prowler.providers.aws.services.firehose.firehose_service import EncryptionStatus
class firehose_stream_encrypted_at_rest(Check):
"""Check if Firehose Streams are encrypted at rest.
This class verifies that all Firehose Streams have at rest encryption enabled by checking if KMS encryption is active and a KMS Key is configured.
"""
def execute(self) -> List[Check_Report_AWS]:
"""Execute the Firehose Stream Encrypted at Rest check.
Iterates over all Firehose Streams and checks if KMS encryption is enabled and a KMS Key is configured.
Returns:
List[Check_Report_AWS]: A list of reports for each Firehose Stream.
"""
findings = []
for stream in firehose_client.delivery_streams.values():
report = Check_Report_AWS(self.metadata())
report.region = stream.region
report.resource_id = stream.name
report.resource_arn = stream.arn
report.resource_tags = stream.tags
report.status = "PASS"
report.status_extended = (
f"Firehose Stream {stream.name} does have at rest encryption enabled."
)
if stream.kms_encryption != EncryptionStatus.ENABLED:
report.status = "FAIL"
report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled."
findings.append(report)
return findings
@@ -25,7 +25,9 @@
"Url": "https://docs.aws.amazon.com/glue/latest/dg/monitor-continuous-logging-enable.html"
}
},
"Categories": [],
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": "This check is being removed since logs for all AWS Glue jobs are now always sent to Amazon CloudWatch."

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