+
+
+#### **Log In**
+Log in with your email and password to start using the Prowler App.
+
+
+
+#### **Add a Provider**
+- Go to `Settings > Cloud Providers` and click `Add Account`.
+- Select the provider you want to scan (AWS, GCP, Azure, Kubernetes).
+- Enter the provider's ID (AWS Account ID, GCP Project ID, Azure Subscription ID, Kubernetes Cluster) and optional alias.
+- Follow the instructions to add your credentials.
+
+#### **Start a Scan**
+After successfully adding and testing your credentials, Prowler will start scanning your cloud environment, click on the `Go to Scans` button to see the progress.
+
+#### **View Results**
+While the scan is running, start exploring the findings in these sections:
+
+- **Overview**: High-level summary of the scans.
+- **Compliance**: Insights into compliance status.
+
+> See more details about the Prowler App usage in the [Prowler App](tutorials/prowler-app.md) section.
+
+???+ note
+ Prowler will automatically scan all configured providers every **24 hours**, ensuring your cloud environment stays continuously monitored.
+
+### Prowler CLI
To run Prowler, you will need to specify the provider (e.g `aws`, `gcp`, `azure` or `kubernetes`):
@@ -289,7 +440,7 @@ You can always use `-h`/`--help` to access to the usage information and all the
prowler --help
```
-### AWS
+#### AWS
Use a custom AWS profile with `-p`/`--profile` and/or AWS regions which you want to audit with `-f`/`--filter-region`:
@@ -302,7 +453,7 @@ prowler aws --profile custom-profile -f us-east-1 eu-south-2
See more details about AWS Authentication in [Requirements](getting-started/requirements.md#aws)
-### Azure
+#### Azure
With Azure you need to specify which auth method is going to be used:
@@ -327,7 +478,7 @@ Prowler by default scans all the subscriptions that is allowed to scan, if you w
prowler azure --az-cli-auth --subscription-ids
+
+
+---
+
+## **Step 2: Log In**
+Once you’ve signed up, log in with your email and password to start using the Prowler App.
+
+
+
+You will see the Overview page with no data yet, so let's start adding a provider to scan your cloud environment.
+
+---
+
+## **Step 3: Add a Provider**
+To run your first scan, you need to add a cloud provider account. Prowler App supports AWS, Azure, GCP, and Kubernetes.
+
+1. Navigate to `Settings > Cloud Providers`.
+2. Click `Add Account` to set up a new provider and provide your credentials:
+
+
+
+---
+
+## **Step 4: Configure the Provider**
+Choose the provider you want to scan from the following options:
+
+
+
+Once you’ve selected a provider, you need to provide the Provider UID:
+
+- **AWS**: Enter your AWS Account ID.
+- **GCP**: Enter your GCP Project ID.
+- **Azure**: Enter your Azure Subscription ID.
+- **Kubernetes**: Enter your Kubernetes Cluster name.
+
+Optionally, provide a **Provider Alias** for easier identification. Follow the instructions provided to add your credentials:
+
+---
+### **Step 4.1: AWS Credentials**
+For AWS, enter your `AWS Account ID` and choose one of the following methods to connect:
+
+#### **Step 4.1.1: IAM Access Keys**
+1. Select `Connect via Credentials`.
+
+
+
+2. Enter your `Access Key ID`, `Secret Access Key` and optionally a `Session Token`:
+
+
+
+#### **Step 4.1.2: IAM Role**
+1. Select `Connect assuming IAM Role`.
+
+
+
+2. Enter the `Role ARN` and any optional field like the AWS Access Keys to assume the role, the `External ID`, the `Role Session Name` or the `Session Duration`:
+
+
+
+---
+
+### **Step 4.2: Azure Credentials**
+For Azure, Prowler App uses a Service Principal to authenticate. See the steps in https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/azure/create-prowler-service-principal/ to create a Service Principal. Then, enter the `Tenant ID`, `Client ID` and `Client Secret` of the Service Principal.
+
+
+
+---
+### **Step 4.3: GCP Credentials**
+To connect your GCP Project, you need to use the Application Default Credentials (ADC) returned by the `gcloud` CLI. Here’s how to set up:
+
+1. Run the following command in your terminal to authenticate with GCP:
+```bash
+gcloud auth application-default login
+```
+2. Once authenticated, get the `Client ID`, `Client Secret` and `Refresh Token` from `~/.config/gcloud/application_default_credentials`.
+3. Paste the `Client ID`, `Client Secret` and `Refresh Token` into the Prowler App.
+
+
+
+---
+### **Step 4.4: Kubernetes Credentials**
+For Kubernetes, Prowler App uses a `kubeconfig` file to authenticate, paste the contents of your `kubeconfig` file into the `Kubeconfig content` field.
+
+By default, the `kubeconfig` file is located at `~/.kube/config`.
+
+
+
+---
+
+## **Step 5: Test Connection**
+After adding your credentials of your cloud account, click the `Launch` button to verify that the Prowler App can successfully connect to your provider:
+
+
+
+
+## **Step 6: Scan started**
+After successfully adding and testing your credentials, Prowler will start scanning your cloud environment, click on the `Go to Scans` button to see the progress:
+
+
+
+???+ note
+ Prowler will automatically scan all configured providers every **24 hours**, ensuring your cloud environment stays continuously monitored.
+---
+
+## **Step 7: Monitor Scan Progress**
+Track the progress of your scan in the `Scans` section:
+
+
+
+---
+
+## **Step 8: Analyze the Findings**
+While the scan is running, start exploring the findings in these sections:
+
+- **Overview**: High-level summary of the scans.
+- **Compliance**: Insights into compliance status.
+- **Issues**: Types of issues detected.
+
+
+
+- **Browse All Findings**: Detailed list of findings detected, where you can filter by severity, service, and more.
diff --git a/mkdocs.yml b/mkdocs.yml
index 170a9c3d9d..e311146609 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -11,6 +11,7 @@ theme:
name: material
favicon: favicon.ico
features:
+ - content.code.copy
- navigation.tabs
- navigation.tabs.sticky
- navigation.sections
@@ -49,6 +50,7 @@ nav:
- Overview: index.md
- Requirements: getting-started/requirements.md
- Tutorials:
+ - Prowler App: tutorials/prowler-app.md
- Miscellaneous: tutorials/misc.md
- Reporting: tutorials/reporting.md
- Compliance: tutorials/compliance.md
diff --git a/permissions/prowler-additions-policy.json b/permissions/prowler-additions-policy.json
index 74c6ba8e6d..aa8e01afa7 100644
--- a/permissions/prowler-additions-policy.json
+++ b/permissions/prowler-additions-policy.json
@@ -7,6 +7,7 @@
"appstream:Describe*",
"appstream:List*",
"backup:List*",
+ "backup:Get*",
"bedrock:List*",
"bedrock:Get*",
"cloudtrail:GetInsightSelectors",
@@ -29,6 +30,7 @@
"glue:GetConnections",
"glue:GetSecurityConfiguration*",
"glue:SearchTables",
+ "glue:GetMLTransforms",
"lambda:GetFunction*",
"logs:FilterLogEvents",
"lightsail:GetRelationalDatabases",
diff --git a/poetry.lock b/poetry.lock
index 456759fa56..3261d5eaa4 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -694,13 +694,13 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
[[package]]
name = "bandit"
-version = "1.7.10"
+version = "1.8.0"
description = "Security oriented static analyser for python code."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
files = [
- {file = "bandit-1.7.10-py3-none-any.whl", hash = "sha256:665721d7bebbb4485a339c55161ac0eedde27d51e638000d91c8c2d68343ad02"},
- {file = "bandit-1.7.10.tar.gz", hash = "sha256:59ed5caf5d92b6ada4bf65bc6437feea4a9da1093384445fed4d472acc6cff7b"},
+ {file = "bandit-1.8.0-py3-none-any.whl", hash = "sha256:b1a61d829c0968aed625381e426aa378904b996529d048f8d908fa28f6b13e38"},
+ {file = "bandit-1.8.0.tar.gz", hash = "sha256:b5bfe55a095abd9fe20099178a7c6c060f844bfd4fe4c76d28e35e4c52b9d31e"},
]
[package.dependencies]
@@ -775,17 +775,17 @@ files = [
[[package]]
name = "boto3"
-version = "1.35.66"
+version = "1.35.71"
description = "The AWS SDK for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "boto3-1.35.66-py3-none-any.whl", hash = "sha256:09a610f8cf4d3c22d4ca69c1f89079e3a1c82805ce94fa0eb4ecdd4d2ba6c4bc"},
- {file = "boto3-1.35.66.tar.gz", hash = "sha256:c392b9168b65e9c23483eaccb5b68d1f960232d7f967a1e00a045ba065ce050d"},
+ {file = "boto3-1.35.71-py3-none-any.whl", hash = "sha256:e2969a246bb3208122b3c349c49cc6604c6fc3fc2b2f65d99d3e8ccd745b0c16"},
+ {file = "boto3-1.35.71.tar.gz", hash = "sha256:3ed7172b3d4fceb6218bb0ec3668c4d40c03690939c2fca4f22bb875d741a07f"},
]
[package.dependencies]
-botocore = ">=1.35.66,<1.36.0"
+botocore = ">=1.35.71,<1.36.0"
jmespath = ">=0.7.1,<2.0.0"
s3transfer = ">=0.10.0,<0.11.0"
@@ -794,13 +794,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "botocore"
-version = "1.35.69"
+version = "1.35.71"
description = "Low-level, data-driven core of boto 3."
optional = false
python-versions = ">=3.8"
files = [
- {file = "botocore-1.35.69-py3-none-any.whl", hash = "sha256:cad8d9305f873404eee4b197d84e60a40975d43cbe1ab63abe893420ddfe6e3c"},
- {file = "botocore-1.35.69.tar.gz", hash = "sha256:f9f23dd76fb247d9b0e8d411d2995e6f847fc451c026f1e58e300f815b0b36eb"},
+ {file = "botocore-1.35.71-py3-none-any.whl", hash = "sha256:fc46e7ab1df3cef66dfba1633f4da77c75e07365b36f03bd64a3793634be8fc1"},
+ {file = "botocore-1.35.71.tar.gz", hash = "sha256:f9fa058e0393660c3fe53c1e044751beb64b586def0bd2212448a7c328b0cbba"},
]
[package.dependencies]
@@ -1099,73 +1099,73 @@ files = [
[[package]]
name = "coverage"
-version = "7.6.7"
+version = "7.6.8"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.9"
files = [
- {file = "coverage-7.6.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:108bb458827765d538abcbf8288599fee07d2743357bdd9b9dad456c287e121e"},
- {file = "coverage-7.6.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c973b2fe4dc445cb865ab369df7521df9c27bf40715c837a113edaa2aa9faf45"},
- {file = "coverage-7.6.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c6b24007c4bcd0b19fac25763a7cac5035c735ae017e9a349b927cfc88f31c1"},
- {file = "coverage-7.6.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acbb8af78f8f91b3b51f58f288c0994ba63c646bc1a8a22ad072e4e7e0a49f1c"},
- {file = "coverage-7.6.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad32a981bcdedb8d2ace03b05e4fd8dace8901eec64a532b00b15217d3677dd2"},
- {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:34d23e28ccb26236718a3a78ba72744212aa383141961dd6825f6595005c8b06"},
- {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e25bacb53a8c7325e34d45dddd2f2fbae0dbc230d0e2642e264a64e17322a777"},
- {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af05bbba896c4472a29408455fe31b3797b4d8648ed0a2ccac03e074a77e2314"},
- {file = "coverage-7.6.7-cp310-cp310-win32.whl", hash = "sha256:796c9b107d11d2d69e1849b2dfe41730134b526a49d3acb98ca02f4985eeff7a"},
- {file = "coverage-7.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:987a8e3da7da4eed10a20491cf790589a8e5e07656b6dc22d3814c4d88faf163"},
- {file = "coverage-7.6.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469"},
- {file = "coverage-7.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99"},
- {file = "coverage-7.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec"},
- {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b"},
- {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a"},
- {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b"},
- {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d"},
- {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4"},
- {file = "coverage-7.6.7-cp311-cp311-win32.whl", hash = "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2"},
- {file = "coverage-7.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f"},
- {file = "coverage-7.6.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f07ff574986bc3edb80e2c36391678a271d555f91fd1d332a1e0f4b5ea4b6ea9"},
- {file = "coverage-7.6.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ed5ee4109258973630c1f9d099c7e72c5c36605029f3a91fe9982c6076c82b"},
- {file = "coverage-7.6.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3e8796434a8106b3ac025fd15417315d7a58ee3e600ad4dbcfddc3f4b14342c"},
- {file = "coverage-7.6.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3b925300484a3294d1c70f6b2b810d6526f2929de954e5b6be2bf8caa1f12c1"},
- {file = "coverage-7.6.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c42ec2c522e3ddd683dec5cdce8e62817afb648caedad9da725001fa530d354"},
- {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0266b62cbea568bd5e93a4da364d05de422110cbed5056d69339bd5af5685433"},
- {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e5f2a0f161d126ccc7038f1f3029184dbdf8f018230af17ef6fd6a707a5b881f"},
- {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c132b5a22821f9b143f87446805e13580b67c670a548b96da945a8f6b4f2efbb"},
- {file = "coverage-7.6.7-cp312-cp312-win32.whl", hash = "sha256:7c07de0d2a110f02af30883cd7dddbe704887617d5c27cf373362667445a4c76"},
- {file = "coverage-7.6.7-cp312-cp312-win_amd64.whl", hash = "sha256:fd49c01e5057a451c30c9b892948976f5d38f2cbd04dc556a82743ba8e27ed8c"},
- {file = "coverage-7.6.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46f21663e358beae6b368429ffadf14ed0a329996248a847a4322fb2e35d64d3"},
- {file = "coverage-7.6.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40cca284c7c310d622a1677f105e8507441d1bb7c226f41978ba7c86979609ab"},
- {file = "coverage-7.6.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77256ad2345c29fe59ae861aa11cfc74579c88d4e8dbf121cbe46b8e32aec808"},
- {file = "coverage-7.6.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87ea64b9fa52bf395272e54020537990a28078478167ade6c61da7ac04dc14bc"},
- {file = "coverage-7.6.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d608a7808793e3615e54e9267519351c3ae204a6d85764d8337bd95993581a8"},
- {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdd94501d65adc5c24f8a1a0eda110452ba62b3f4aeaba01e021c1ed9cb8f34a"},
- {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82c809a62e953867cf57e0548c2b8464207f5f3a6ff0e1e961683e79b89f2c55"},
- {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb684694e99d0b791a43e9fc0fa58efc15ec357ac48d25b619f207c41f2fd384"},
- {file = "coverage-7.6.7-cp313-cp313-win32.whl", hash = "sha256:963e4a08cbb0af6623e61492c0ec4c0ec5c5cf74db5f6564f98248d27ee57d30"},
- {file = "coverage-7.6.7-cp313-cp313-win_amd64.whl", hash = "sha256:14045b8bfd5909196a90da145a37f9d335a5d988a83db34e80f41e965fb7cb42"},
- {file = "coverage-7.6.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f2c7a045eef561e9544359a0bf5784b44e55cefc7261a20e730baa9220c83413"},
- {file = "coverage-7.6.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dd4e4a49d9c72a38d18d641135d2fb0bdf7b726ca60a103836b3d00a1182acd"},
- {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c95e0fa3d1547cb6f021ab72f5c23402da2358beec0a8e6d19a368bd7b0fb37"},
- {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f63e21ed474edd23f7501f89b53280014436e383a14b9bd77a648366c81dce7b"},
- {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead9b9605c54d15be228687552916c89c9683c215370c4a44f1f217d2adcc34d"},
- {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0573f5cbf39114270842d01872952d301027d2d6e2d84013f30966313cadb529"},
- {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e2c8e3384c12dfa19fa9a52f23eb091a8fad93b5b81a41b14c17c78e23dd1d8b"},
- {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:70a56a2ec1869e6e9fa69ef6b76b1a8a7ef709972b9cc473f9ce9d26b5997ce3"},
- {file = "coverage-7.6.7-cp313-cp313t-win32.whl", hash = "sha256:dbba8210f5067398b2c4d96b4e64d8fb943644d5eb70be0d989067c8ca40c0f8"},
- {file = "coverage-7.6.7-cp313-cp313t-win_amd64.whl", hash = "sha256:dfd14bcae0c94004baba5184d1c935ae0d1231b8409eb6c103a5fd75e8ecdc56"},
- {file = "coverage-7.6.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37a15573f988b67f7348916077c6d8ad43adb75e478d0910957394df397d2874"},
- {file = "coverage-7.6.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b6cce5c76985f81da3769c52203ee94722cd5d5889731cd70d31fee939b74bf0"},
- {file = "coverage-7.6.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ab9763d291a17b527ac6fd11d1a9a9c358280adb320e9c2672a97af346ac2c"},
- {file = "coverage-7.6.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cf96ceaa275f071f1bea3067f8fd43bec184a25a962c754024c973af871e1b7"},
- {file = "coverage-7.6.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee9cf6b0134d6f932d219ce253ef0e624f4fa588ee64830fcba193269e4daa3"},
- {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2bc3e45c16564cc72de09e37413262b9f99167803e5e48c6156bccdfb22c8327"},
- {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:623e6965dcf4e28a3debaa6fcf4b99ee06d27218f46d43befe4db1c70841551c"},
- {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:850cfd2d6fc26f8346f422920ac204e1d28814e32e3a58c19c91980fa74d8289"},
- {file = "coverage-7.6.7-cp39-cp39-win32.whl", hash = "sha256:c296263093f099da4f51b3dff1eff5d4959b527d4f2f419e16508c5da9e15e8c"},
- {file = "coverage-7.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:90746521206c88bdb305a4bf3342b1b7316ab80f804d40c536fc7d329301ee13"},
- {file = "coverage-7.6.7-pp39.pp310-none-any.whl", hash = "sha256:0ddcb70b3a3a57581b450571b31cb774f23eb9519c2aaa6176d3a84c9fc57671"},
- {file = "coverage-7.6.7.tar.gz", hash = "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24"},
+ {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"},
+ {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"},
+ {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"},
+ {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"},
+ {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"},
+ {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"},
+ {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"},
+ {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"},
+ {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"},
+ {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"},
+ {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"},
+ {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"},
+ {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"},
+ {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"},
+ {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"},
+ {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"},
+ {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"},
+ {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"},
+ {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"},
+ {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"},
+ {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"},
+ {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"},
+ {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"},
+ {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"},
+ {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"},
+ {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"},
+ {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"},
+ {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"},
+ {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"},
+ {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"},
+ {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"},
+ {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"},
+ {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"},
+ {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"},
+ {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"},
+ {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"},
+ {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"},
+ {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"},
+ {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"},
+ {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"},
+ {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"},
+ {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"},
+ {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"},
+ {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"},
+ {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"},
+ {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"},
+ {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"},
+ {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"},
+ {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"},
+ {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"},
+ {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"},
+ {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"},
+ {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"},
+ {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"},
+ {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"},
+ {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"},
+ {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"},
+ {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"},
+ {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"},
+ {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"},
+ {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"},
+ {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"},
]
[package.dependencies]
@@ -2583,13 +2583,13 @@ dev = ["click", "codecov", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkd
[[package]]
name = "mkdocs-material"
-version = "9.5.45"
+version = "9.5.46"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs_material-9.5.45-py3-none-any.whl", hash = "sha256:a9be237cfd0be14be75f40f1726d83aa3a81ce44808dc3594d47a7a592f44547"},
- {file = "mkdocs_material-9.5.45.tar.gz", hash = "sha256:286489cf0beca4a129d91d59d6417419c63bceed1ce5cd0ec1fc7e1ebffb8189"},
+ {file = "mkdocs_material-9.5.46-py3-none-any.whl", hash = "sha256:98f0a2039c62e551a68aad0791a8d41324ff90c03a6e6cea381a384b84908b83"},
+ {file = "mkdocs_material-9.5.46.tar.gz", hash = "sha256:ae2043f4238e572f9a40e0b577f50400d6fc31e2fef8ea141800aebf3bd273d7"},
]
[package.dependencies]
@@ -5194,4 +5194,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<3.13"
-content-hash = "b1a5cce3381c4b6a115913bdeb69546cbedecc60803e5b33798187aa82641e77"
+content-hash = "1acc901866ecfc2c0f3576b9e442d7a3b6e6522cac3d4d1b9301ed4232755cba"
diff --git a/prowler/config/config.yaml b/prowler/config/config.yaml
index 7c42c521c0..8b6947b458 100644
--- a/prowler/config/config.yaml
+++ b/prowler/config/config.yaml
@@ -42,7 +42,7 @@ aws:
"amazon-elb"
]
# aws.ec2_securitygroup_allow_ingress_from_internet_to_high_risk_tcp_ports
- ec2_sg_high_risk_ports:
+ ec2_high_risk_ports:
[
25,
110,
diff --git a/prowler/lib/check/check.py b/prowler/lib/check/check.py
index fc648ffc04..d760702fa8 100644
--- a/prowler/lib/check/check.py
+++ b/prowler/lib/check/check.py
@@ -556,19 +556,6 @@ def execute_checks(
bar()
bar.title = f"-> {Fore.GREEN}Scan completed!{Style.RESET_ALL}"
- # Custom report interface
- if os.environ.get("PROWLER_REPORT_LIB_PATH"):
- try:
- logger.info("Using custom report interface ...")
- lib = os.environ["PROWLER_REPORT_LIB_PATH"]
- outputs_module = importlib.import_module(lib)
- custom_report_interface = getattr(outputs_module, "report")
-
- # TODO: review this call and see if we can remove the global_provider.output_options since it is contained in the global_provider
- custom_report_interface(check_findings, output_options, global_provider)
- except Exception:
- sys.exit(1)
-
return all_findings
diff --git a/prowler/lib/check/checks_loader.py b/prowler/lib/check/checks_loader.py
index 8d5ac97ac4..ff54ccc8d1 100644
--- a/prowler/lib/check/checks_loader.py
+++ b/prowler/lib/check/checks_loader.py
@@ -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)
diff --git a/prowler/lib/check/models.py b/prowler/lib/check/models.py
index 446440b6aa..b060c58e86 100644
--- a/prowler/lib/check/models.py
+++ b/prowler/lib/check/models.py
@@ -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()
diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json
index 7d6567c5e6..cfc75a2e06 100644
--- a/prowler/providers/aws/aws_regions_by_service.json
+++ b/prowler/providers/aws/aws_regions_by_service.json
@@ -7404,25 +7404,6 @@
"aws-us-gov": []
}
},
- "nimble": {
- "regions": {
- "aws": [
- "ap-northeast-1",
- "ap-southeast-1",
- "ap-southeast-2",
- "ca-central-1",
- "eu-central-1",
- "eu-north-1",
- "eu-west-1",
- "eu-west-2",
- "us-east-1",
- "us-east-2",
- "us-west-2"
- ],
- "aws-cn": [],
- "aws-us-gov": []
- }
- },
"notifications": {
"regions": {
"aws": [
@@ -9284,10 +9265,7 @@
"us-west-2"
],
"aws-cn": [],
- "aws-us-gov": [
- "us-gov-east-1",
- "us-gov-west-1"
- ]
+ "aws-us-gov": []
}
},
"sagemaker-runtime": {
@@ -10098,6 +10076,15 @@
]
}
},
+ "socialmessaging": {
+ "regions": {
+ "aws": [
+ "eu-central-1"
+ ],
+ "aws-cn": [],
+ "aws-us-gov": []
+ }
+ },
"sqs": {
"regions": {
"aws": [
diff --git a/prowler/providers/aws/services/rds/rds_service.py b/prowler/providers/aws/services/rds/rds_service.py
index b4dd5d66ce..60d24b1cb1 100644
--- a/prowler/providers/aws/services/rds/rds_service.py
+++ b/prowler/providers/aws/services/rds/rds_service.py
@@ -446,7 +446,7 @@ class RDS(AWSService):
arn=arn,
sns_topic_arn=event["SnsTopicArn"],
status=event["Status"],
- source_type=event["SourceType"],
+ source_type=event.get("SourceType", ""),
source_id=event.get("SourceIdsList", []),
event_list=event.get("EventCategoriesList", []),
enabled=event["Enabled"],
diff --git a/prowler/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible.py b/prowler/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible.py
index d8a6a41da4..d63d1a5e28 100644
--- a/prowler/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible.py
+++ b/prowler/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible.py
@@ -18,12 +18,7 @@ class containerregistry_not_publicly_accessible(Check):
report.status = "FAIL"
report.status_extended = f"Container Registry {container_registry_info.name} from subscription {subscription} allows unrestricted network access."
- if (
- getattr(
- container_registry_info.network_rule_set, "default_action", ""
- ).lower()
- == "deny"
- ):
+ if not container_registry_info.public_network_access:
report.status = "PASS"
report.status_extended = f"Container Registry {container_registry_info.name} from subscription {subscription} does not allow unrestricted network access."
diff --git a/prowler/providers/azure/services/containerregistry/containerregistry_service.py b/prowler/providers/azure/services/containerregistry/containerregistry_service.py
index 6c3840febc..189c89c642 100644
--- a/prowler/providers/azure/services/containerregistry/containerregistry_service.py
+++ b/prowler/providers/azure/services/containerregistry/containerregistry_service.py
@@ -37,8 +37,13 @@ class ContainerRegistry(AzureService):
resource_group=resource_group,
sku=getattr(registry.sku, "name", ""),
login_server=getattr(registry, "login_server", ""),
- public_network_access=getattr(
- registry, "public_network_access", ""
+ public_network_access=(
+ False
+ if getattr(
+ registry, "public_network_access" "Enabled"
+ )
+ == "Disabled"
+ else True
),
admin_user_enabled=getattr(
registry, "admin_user_enabled", False
@@ -93,7 +98,7 @@ class ContainerRegistryInfo:
resource_group: str
sku: str
login_server: str
- public_network_access: str
+ public_network_access: bool
admin_user_enabled: bool
network_rule_set: NetworkRuleSet
monitor_diagnostic_settings: list[DiagnosticSetting]
diff --git a/prowler/providers/gcp/lib/service/service.py b/prowler/providers/gcp/lib/service/service.py
index dbb7f22638..9763230153 100644
--- a/prowler/providers/gcp/lib/service/service.py
+++ b/prowler/providers/gcp/lib/service/service.py
@@ -55,7 +55,9 @@ class GCPService:
project_ids = []
for project_id in audited_project_ids:
try:
- client = discovery.build("serviceusage", "v1")
+ client = discovery.build(
+ "serviceusage", "v1", credentials=self.credentials
+ )
request = client.services().get(
name=f"projects/{project_id}/services/{self.service}.googleapis.com"
)
diff --git a/prowler/providers/kubernetes/kubernetes_provider.py b/prowler/providers/kubernetes/kubernetes_provider.py
index 9182d3a4ca..0f11a41fb0 100644
--- a/prowler/providers/kubernetes/kubernetes_provider.py
+++ b/prowler/providers/kubernetes/kubernetes_provider.py
@@ -1,4 +1,5 @@
import os
+from typing import Union
from colorama import Fore, Style
from kubernetes.client.exceptions import ApiException
@@ -74,14 +75,14 @@ class KubernetesProvider(Provider):
fixer_config: dict = {},
mutelist_path: str = None,
mutelist_content: dict = {},
- kubeconfig_content: dict = None,
+ kubeconfig_content: Union[dict, str] = None,
):
"""
Initializes the KubernetesProvider instance.
Args:
kubeconfig_file (str): Path to the kubeconfig file.
- kubeconfig_content (dict): Content of the kubeconfig file.
+ kubeconfig_content (str or dict): Content of the kubeconfig file.
context (str): Context name.
namespace (list): List of namespaces.
config_content (dict): Audit configuration.
@@ -224,7 +225,7 @@ class KubernetesProvider(Provider):
@staticmethod
def setup_session(
kubeconfig_file: str = None,
- kubeconfig_content: dict = None,
+ kubeconfig_content: Union[dict, str] = None,
context: str = None,
) -> KubernetesSession:
"""
@@ -232,7 +233,7 @@ class KubernetesProvider(Provider):
Args:
kubeconfig_file (str): Path to the kubeconfig file.
- kubeconfig_content (dict): Content of the kubeconfig file.
+ kubeconfig_content (str or dict): Content of the kubeconfig file.
context (str): Context name.
Returns:
@@ -243,14 +244,20 @@ class KubernetesProvider(Provider):
KubernetesInvalidProviderIdError: If the provider ID is invalid.
KubernetesSetUpSessionError: If an error occurs while setting up the session.
"""
- logger.info(f"Using kubeconfig file: {kubeconfig_file}")
try:
if kubeconfig_content:
- config.load_kube_config_from_dict(
- safe_load(kubeconfig_content), context=context
- )
-
+ logger.info("Using kubeconfig content...")
+ config_data = safe_load(kubeconfig_content)
+ config.load_kube_config_from_dict(config_data, context=context)
+ if context:
+ contexts = config_data.get("contexts", [])
+ for context_item in contexts:
+ if context_item["name"] == context:
+ context = context_item
+ else:
+ context = config_data.get("contexts", [])[0]
else:
+ logger.info(f"Using kubeconfig file: {kubeconfig_file}...")
kubeconfig_file = (
kubeconfig_file if kubeconfig_file else "~/.kube/config"
)
@@ -273,17 +280,19 @@ class KubernetesProvider(Provider):
return KubernetesSession(
api_client=client.ApiClient(), context=context
)
- if context:
- contexts = config.list_kube_config_contexts(
- config_file=kubeconfig_file
- )[0]
- for context_item in contexts:
- if context_item["name"] == context:
- context = context_item
- else:
- context = config.list_kube_config_contexts(config_file=kubeconfig_file)[
- 1
- ]
+ if context:
+ contexts = config.list_kube_config_contexts(
+ config_file=kubeconfig_file
+ )[0]
+ for context_item in contexts:
+ if context_item["name"] == context:
+ context = context_item
+ else:
+ # If no context is provided, use the active context in the kubeconfig file
+ # The first element is the list of contexts, the second is the active context
+ context = config.list_kube_config_contexts(
+ config_file=kubeconfig_file
+ )[1]
return KubernetesSession(api_client=client.ApiClient(), context=context)
except parser.ParserError as parser_error:
@@ -318,7 +327,7 @@ class KubernetesProvider(Provider):
@staticmethod
def test_connection(
kubeconfig_file: str = "~/.kube/config",
- kubeconfig_content: dict = None,
+ kubeconfig_content: Union[dict, str] = None,
namespace: str = None,
provider_id: str = None,
raise_on_exception: bool = True,
@@ -328,7 +337,7 @@ class KubernetesProvider(Provider):
Args:
kubeconfig_file (str): Path to the kubeconfig file.
- kubeconfig_content (dict): Content of the kubeconfig file.
+ kubeconfig_content (str or dict): Content of the kubeconfig file.
namespace (str): Namespace name.
provider_id (str): Provider ID to use, in this case, the Kubernetes context.
raise_on_exception (bool): Whether to raise an exception on error.
@@ -352,7 +361,7 @@ class KubernetesProvider(Provider):
... )
- Using the kubeconfig content:
>>> connection = KubernetesProvider.test_connection(
- ... kubeconfig_content={"kubecofig": "content"},
+ ... kubeconfig_content="kubeconfig content",
... namespace="default",
... provider_id="my-context",
... raise_on_exception=True,
diff --git a/pyproject.toml b/pyproject.toml
index f0354fdadc..c72a5ab9a7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -48,8 +48,8 @@ azure-mgmt-storage = "21.2.1"
azure-mgmt-subscription = "3.1.1"
azure-mgmt-web = "7.3.1"
azure-storage-blob = "12.24.0"
-boto3 = "1.35.66"
-botocore = "1.35.69"
+boto3 = "1.35.71"
+botocore = "1.35.71"
colorama = "0.4.6"
cryptography = "43.0.1"
dash = "2.18.2"
@@ -75,9 +75,9 @@ tabulate = "0.9.0"
tzlocal = "5.2"
[tool.poetry.group.dev.dependencies]
-bandit = "1.7.10"
+bandit = "1.8.0"
black = "24.10.0"
-coverage = "7.6.7"
+coverage = "7.6.8"
docker = "7.1.0"
flake8 = "7.1.1"
freezegun = "1.5.1"
@@ -100,7 +100,7 @@ optional = true
[tool.poetry.group.docs.dependencies]
mkdocs = "1.6.1"
mkdocs-git-revision-date-localized-plugin = "1.3.0"
-mkdocs-material = "9.5.45"
+mkdocs-material = "9.5.46"
mkdocs-material-extensions = "1.3.1"
[tool.poetry.scripts]
diff --git a/tests/config/config_test.py b/tests/config/config_test.py
index 98e1e5f43e..f79ef50eff 100644
--- a/tests/config/config_test.py
+++ b/tests/config/config_test.py
@@ -79,7 +79,7 @@ config_aws = {
"max_ec2_instance_age_in_days": 180,
"ec2_allowed_interface_types": ["api_gateway_managed", "vpc_endpoint"],
"ec2_allowed_instance_owners": ["amazon-elb"],
- "ec2_sg_high_risk_ports": [
+ "ec2_high_risk_ports": [
25,
110,
135,
diff --git a/tests/config/fixtures/config.yaml b/tests/config/fixtures/config.yaml
index d769b83404..54b1c011b3 100644
--- a/tests/config/fixtures/config.yaml
+++ b/tests/config/fixtures/config.yaml
@@ -42,7 +42,7 @@ aws:
"amazon-elb"
]
# aws.ec2_securitygroup_allow_ingress_from_internet_to_high_risk_tcp_ports
- ec2_sg_high_risk_ports:
+ ec2_high_risk_ports:
[
25,
110,
diff --git a/tests/lib/check/check_loader_test.py b/tests/lib/check/check_loader_test.py
index 72080fc560..a122d76ff5 100644
--- a/tests/lib/check/check_loader_test.py
+++ b/tests/lib/check/check_loader_test.py
@@ -14,11 +14,13 @@ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME_CUSTOM_ALIAS = (
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_SEVERITY = "medium"
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME_SERVICE = "s3"
+CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME = "cloudtrail_threat_detection_enumeration"
+
class TestCheckLoader:
provider = "aws"
- def get_custom_check_metadata(self):
+ def get_custom_check_s3_metadata(self):
return CheckMetadata(
Provider="aws",
CheckID=S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME,
@@ -52,9 +54,37 @@ class TestCheckLoader:
Compliance=[],
)
+ def get_threat_detection_check_metadata(self):
+ return CheckMetadata(
+ Provider="aws",
+ CheckID=CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME,
+ CheckTitle="Ensure there are no potential enumeration threats in CloudTrail",
+ CheckType=[],
+ ServiceName="cloudtrail",
+ SubServiceName="",
+ ResourceIdTemplate="arn:partition:service:region:account-id:resource-id",
+ Severity="critical",
+ ResourceType="AwsCloudTrailTrail",
+ Description="This check ensures that there are no potential enumeration threats in CloudTrail.",
+ Risk="Potential enumeration threats in CloudTrail can lead to unauthorized access to resources.",
+ RelatedUrl="",
+ Remediation=Remediation(
+ Code=Code(CLI="", NativeIaC="", Other="", Terraform=""),
+ Recommendation=Recommendation(
+ Text="To remediate this issue, ensure that there are no potential enumeration threats in CloudTrail.",
+ Url="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html#cloudtrail-concepts-logging-data-events",
+ ),
+ ),
+ Categories=["threat-detection"],
+ DependsOn=[],
+ RelatedTo=[],
+ Notes="",
+ Compliance=[],
+ )
+
def test_load_checks_to_execute(self):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
assert {S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME} == load_checks_to_execute(
@@ -64,7 +94,7 @@ class TestCheckLoader:
def test_load_checks_to_execute_with_check_list(self):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
check_list = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME]
@@ -76,7 +106,7 @@ class TestCheckLoader:
def test_load_checks_to_execute_with_severities(self):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
severities = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_SEVERITY]
@@ -88,7 +118,7 @@ class TestCheckLoader:
def test_load_checks_to_execute_with_severities_and_services(self):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
service_list = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME_SERVICE]
severities = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_SEVERITY]
@@ -104,7 +134,7 @@ class TestCheckLoader:
self,
):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
service_list = ["ec2"]
severities = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_SEVERITY]
@@ -120,7 +150,7 @@ class TestCheckLoader:
self,
):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
checks_file = "path/to/test_file"
with patch(
@@ -137,7 +167,7 @@ class TestCheckLoader:
self,
):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
service_list = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME_SERVICE]
@@ -178,7 +208,7 @@ class TestCheckLoader:
self,
):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
categories = {"internet-exposed"}
@@ -190,7 +220,7 @@ class TestCheckLoader:
def test_load_checks_to_execute_no_bulk_checks_metadata(self):
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
with patch(
"prowler.lib.check.checks_loader.CheckMetadata.get_bulk",
@@ -221,7 +251,7 @@ class TestCheckLoader:
compliance_frameworks = ["soc2_aws"]
bulk_checks_metatada = {
- S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
+ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
}
with patch(
"prowler.lib.check.checks_loader.CheckMetadata.get_bulk",
@@ -248,3 +278,27 @@ class TestCheckLoader:
assert {"check1_name", "check2_name"} == update_checks_to_execute_with_aliases(
checks_to_execute, check_aliases
)
+
+ def test_threat_detection_category(self):
+ bulk_checks_metatada = {
+ CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME: self.get_threat_detection_check_metadata()
+ }
+ categories = {"threat-detection"}
+
+ assert {CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME} == load_checks_to_execute(
+ bulk_checks_metadata=bulk_checks_metatada,
+ categories=categories,
+ provider=self.provider,
+ )
+
+ def test_discard_threat_detection_checks(self):
+ bulk_checks_metatada = {
+ CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME: self.get_threat_detection_check_metadata()
+ }
+ categories = {}
+
+ assert set() == load_checks_to_execute(
+ bulk_checks_metadata=bulk_checks_metatada,
+ categories=categories,
+ provider=self.provider,
+ )
diff --git a/tests/lib/check/models_test.py b/tests/lib/check/models_test.py
index 7cf470c7e6..414de90288 100644
--- a/tests/lib/check/models_test.py
+++ b/tests/lib/check/models_test.py
@@ -32,6 +32,35 @@ mock_metadata = CheckMetadata(
Compliance=[],
)
+mock_metadata_lambda = CheckMetadata(
+ Provider="aws",
+ CheckID="awslambda_function_url_public",
+ CheckTitle="Check 1",
+ CheckType=["type1"],
+ ServiceName="lambda",
+ SubServiceName="subservice1",
+ ResourceIdTemplate="template1",
+ Severity="high",
+ ResourceType="resource1",
+ Description="Description 1",
+ Risk="risk1",
+ RelatedUrl="url1",
+ Remediation={
+ "Code": {
+ "CLI": "cli1",
+ "NativeIaC": "native1",
+ "Other": "other1",
+ "Terraform": "terraform1",
+ },
+ "Recommendation": {"Text": "text1", "Url": "url1"},
+ },
+ Categories=["categoryone"],
+ DependsOn=["dependency1"],
+ RelatedTo=["related1"],
+ Notes="notes1",
+ Compliance=[],
+)
+
class TestCheckMetada:
@@ -188,6 +217,46 @@ class TestCheckMetada:
# Assertions
assert result == {"accessanalyzer_enabled"}
+ @mock.patch("prowler.lib.check.models.load_check_metadata")
+ @mock.patch("prowler.lib.check.models.recover_checks_from_provider")
+ def test_list_by_service_lambda(self, mock_recover_checks, mock_load_metadata):
+ # Mock the return value of recover_checks_from_provider
+ mock_recover_checks.return_value = [
+ ("awslambda_function_url_public", "/path/to/awslambda_function_url_public")
+ ]
+
+ # Mock the return value of load_check_metadata
+ mock_load_metadata.return_value = mock_metadata_lambda
+
+ bulk_metadata = CheckMetadata.get_bulk(provider="aws")
+
+ result = CheckMetadata.list(
+ bulk_checks_metadata=bulk_metadata, service="lambda"
+ )
+
+ # Assertions
+ assert result == {"awslambda_function_url_public"}
+
+ @mock.patch("prowler.lib.check.models.load_check_metadata")
+ @mock.patch("prowler.lib.check.models.recover_checks_from_provider")
+ def test_list_by_service_awslambda(self, mock_recover_checks, mock_load_metadata):
+ # Mock the return value of recover_checks_from_provider
+ mock_recover_checks.return_value = [
+ ("awslambda_function_url_public", "/path/to/awslambda_function_url_public")
+ ]
+
+ # Mock the return value of load_check_metadata
+ mock_load_metadata.return_value = mock_metadata_lambda
+
+ bulk_metadata = CheckMetadata.get_bulk(provider="aws")
+
+ result = CheckMetadata.list(
+ bulk_checks_metadata=bulk_metadata, service="awslambda"
+ )
+
+ # Assertions
+ assert result == {"awslambda_function_url_public"}
+
@mock.patch("prowler.lib.check.models.load_check_metadata")
@mock.patch("prowler.lib.check.models.recover_checks_from_provider")
def test_list_by_service_invalid(self, mock_recover_checks, mock_load_metadata):
diff --git a/tests/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible_test.py b/tests/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible_test.py
index 827c889b73..8239aa5b70 100644
--- a/tests/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible_test.py
+++ b/tests/providers/azure/services/containerregistry/containerregistry_not_publicly_accessible/containerregistry_not_publicly_accessible_test.py
@@ -57,7 +57,7 @@ class Test_containerregistry_not_publicly_accessible:
resource_group="mock_resource_group",
sku="Basic",
login_server="mock_login_server.azurecr.io",
- public_network_access="Enabled",
+ public_network_access=True,
admin_user_enabled=True,
network_rule_set=NetworkRuleSet(default_action="Allow"),
private_endpoint_connections=[],
@@ -131,7 +131,7 @@ class Test_containerregistry_not_publicly_accessible:
resource_group="mock_resource_group",
sku="Basic",
login_server="mock_login_server.azurecr.io",
- public_network_access="Enabled",
+ public_network_access=False,
admin_user_enabled=False,
network_rule_set=NetworkRuleSet(default_action="Deny"),
private_endpoint_connections=[],
diff --git a/tests/providers/azure/services/containerregistry/containerregistry_service_test.py b/tests/providers/azure/services/containerregistry/containerregistry_service_test.py
index 31ca7cea9a..b6ff17fb16 100644
--- a/tests/providers/azure/services/containerregistry/containerregistry_service_test.py
+++ b/tests/providers/azure/services/containerregistry/containerregistry_service_test.py
@@ -32,7 +32,7 @@ class TestContainerRegistryService:
resource_group="mock_resource_group",
sku="Basic",
login_server="mock_login_server.azurecr.io",
- public_network_access="Enabled",
+ public_network_access=False,
admin_user_enabled=True,
network_rule_set=None,
private_endpoint_connections=[],
@@ -71,7 +71,7 @@ class TestContainerRegistryService:
assert registry_info.resource_group == "mock_resource_group"
assert registry_info.sku == "Basic"
assert registry_info.login_server == "mock_login_server.azurecr.io"
- assert registry_info.public_network_access == "Enabled"
+ assert not registry_info.public_network_access
assert registry_info.admin_user_enabled is True
assert isinstance(registry_info.monitor_diagnostic_settings, list)
diff --git a/tests/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled_test.py b/tests/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled_test.py
index 0f1311cf09..f79e7ad725 100644
--- a/tests/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled_test.py
+++ b/tests/providers/gcp/services/kms/kms_key_rotation_enabled/kms_key_rotation_enabled_test.py
@@ -1,3 +1,4 @@
+import datetime
from unittest import mock
from tests.providers.gcp.gcp_fixtures import (
@@ -239,7 +240,10 @@ class Test_kms_key_rotation_enabled:
project_id=GCP_PROJECT_ID,
key_ring=keyring.name,
location=keylocation.name,
- next_rotation_time="2025-09-01T00:00:00Z",
+ # Next rotation time of now + 100 days
+ next_rotation_time=(
+ datetime.datetime.now() - datetime.timedelta(days=+100)
+ ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
members=["user:jane@example.com"],
)
]
@@ -296,7 +300,10 @@ class Test_kms_key_rotation_enabled:
project_id=GCP_PROJECT_ID,
key_ring=keyring.name,
location=keylocation.name,
- next_rotation_time="2024-09-01T00:00:00Z",
+ # Next rotation time of now + 30 days
+ next_rotation_time=(
+ datetime.datetime.now() - datetime.timedelta(days=+30)
+ ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
members=["user:jane@example.com"],
)
]
@@ -352,7 +359,10 @@ class Test_kms_key_rotation_enabled:
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
project_id=GCP_PROJECT_ID,
rotation_period="8776000s",
- next_rotation_time="2025-09-01T00:00:00Z",
+ # Next rotation time of now + 100 days
+ next_rotation_time=(
+ datetime.datetime.now() - datetime.timedelta(days=+100)
+ ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
key_ring=keyring.name,
location=keylocation.name,
members=["user:jane@example.com"],
@@ -412,7 +422,10 @@ class Test_kms_key_rotation_enabled:
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
project_id=GCP_PROJECT_ID,
rotation_period="8776000s",
- next_rotation_time="2024-09-01T00:00:00Z",
+ # Next rotation time of now + 30 days
+ next_rotation_time=(
+ datetime.datetime.now() - datetime.timedelta(days=+30)
+ ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
key_ring=keyring.name,
location=keylocation.name,
members=["user:jane@example.com"],
@@ -470,7 +483,10 @@ class Test_kms_key_rotation_enabled:
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
project_id=GCP_PROJECT_ID,
rotation_period="7776000s",
- next_rotation_time="2025-09-01T00:00:00Z",
+ # Next rotation time of now + 100 days
+ next_rotation_time=(
+ datetime.datetime.now() - datetime.timedelta(days=+100)
+ ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
key_ring=keyring.name,
location=keylocation.name,
members=["user:jane@example.com"],
@@ -530,7 +546,10 @@ class Test_kms_key_rotation_enabled:
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
project_id=GCP_PROJECT_ID,
rotation_period="7776000s",
- next_rotation_time="2024-09-01T00:00:00Z",
+ # Next rotation time of now + 30 days
+ next_rotation_time=(
+ datetime.datetime.now() - datetime.timedelta(days=+30)
+ ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
key_ring=keyring.name,
location=keylocation.name,
members=["user:jane@example.com"],
@@ -588,7 +607,10 @@ class Test_kms_key_rotation_enabled:
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
project_id=GCP_PROJECT_ID,
rotation_period="7776000s",
- next_rotation_time="2025-07-06T22:00:00.561275Z",
+ # Next rotation time of now + 100 days
+ next_rotation_time=(
+ datetime.datetime.now() - datetime.timedelta(days=+100)
+ ).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
key_ring=keyring.name,
location=keylocation.name,
members=["user:jane@example.com"],
diff --git a/ui/actions/auth/auth.ts b/ui/actions/auth/auth.ts
index d5ec199b06..d39314d2da 100644
--- a/ui/actions/auth/auth.ts
+++ b/ui/actions/auth/auth.ts
@@ -37,6 +37,10 @@ export async function authenticate(
credentials: "Incorrect email or password",
},
};
+ case "CallbackRouteError":
+ return {
+ message: error.cause?.err?.message,
+ };
default:
return {
message: "Unknown error",
@@ -89,7 +93,14 @@ export const createNewUser = async (
return parsedResponse;
} catch (error) {
- return { errors: [{ detail: "Network error or server is unreachable" }] };
+ return {
+ errors: [
+ {
+ source: { pointer: "" },
+ detail: "Network error or server is unreachable",
+ },
+ ],
+ };
}
};
@@ -145,22 +156,31 @@ export const getUserByMe = async (accessToken: string) => {
},
});
- if (!response.ok) throw new Error("Error in trying to get user by me");
-
const parsedResponse = await response.json();
+ if (!response.ok) {
+ // Handle different HTTP error codes
+ switch (response.status) {
+ case 401:
+ throw new Error("Invalid or expired token");
+ case 403:
+ throw new Error(parsedResponse.errors?.[0]?.detail);
+ case 404:
+ throw new Error("User not found");
+ default:
+ throw new Error(
+ parsedResponse.errors?.[0]?.detail || "Unknown error",
+ );
+ }
+ }
- const name = parsedResponse.data.attributes.name;
- const email = parsedResponse.data.attributes.email;
- const company = parsedResponse.data.attributes.company_name;
- const dateJoined = parsedResponse.data.attributes.date_joined;
return {
- name,
- email,
- company,
- dateJoined,
+ name: parsedResponse.data.attributes.name,
+ email: parsedResponse.data.attributes.email,
+ company: parsedResponse.data.attributes.company_name,
+ dateJoined: parsedResponse.data.attributes.date_joined,
};
- } catch (error) {
- throw new Error("Error in trying to get user by me");
+ } catch (error: any) {
+ throw new Error(error.message || "Network error or server unreachable");
}
};
diff --git a/ui/actions/findings/findings.ts b/ui/actions/findings/findings.ts
index 9fc89b0907..3b035f84ab 100644
--- a/ui/actions/findings/findings.ts
+++ b/ui/actions/findings/findings.ts
@@ -8,6 +8,7 @@ import { parseStringify } from "@/lib";
export const getFindings = async ({
page = 1,
+ pageSize = 10,
query = "",
sort = "",
filters = {},
@@ -21,10 +22,11 @@ export const getFindings = async ({
const url = new URL(`${keyServer}/findings?include=resources.provider,scan`);
if (page) url.searchParams.append("page[number]", page.toString());
+ if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
+
if (query) url.searchParams.append("filter[search]", query);
if (sort) url.searchParams.append("sort", sort);
- // Handle multiple filters
Object.entries(filters).forEach(([key, value]) => {
if (key !== "filter[search]") {
url.searchParams.append(key, String(value));
@@ -43,7 +45,51 @@ export const getFindings = async ({
revalidatePath("/findings");
return parsedData;
} catch (error) {
+ // eslint-disable-next-line no-console
console.error("Error fetching findings:", error);
return undefined;
}
};
+
+export const getServicesRegions = async ({
+ query = "",
+ sort = "",
+ filters = {},
+}) => {
+ const session = await auth();
+
+ const keyServer = process.env.API_BASE_URL;
+ const url = new URL(`${keyServer}/findings/findings_services_regions`);
+
+ if (query) url.searchParams.append("filter[search]", query);
+ if (sort) url.searchParams.append("sort", sort);
+
+ Object.entries(filters).forEach(([key, value]) => {
+ if (key !== "filter[search]") {
+ url.searchParams.append(key, String(value));
+ }
+ });
+
+ try {
+ const response = await fetch(url.toString(), {
+ headers: {
+ Accept: "application/vnd.api+json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(
+ `Failed to fetch services regions: ${response.statusText}`,
+ );
+ }
+
+ const data = await response.json();
+ const parsedData = parseStringify(data);
+ return parsedData;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("Error fetching services regions:", error);
+ return undefined;
+ }
+};
diff --git a/ui/actions/overview/overview.ts b/ui/actions/overview/overview.ts
index 8d83ab7088..2095a9f32f 100644
--- a/ui/actions/overview/overview.ts
+++ b/ui/actions/overview/overview.ts
@@ -39,7 +39,7 @@ export const getProvidersOverview = async ({
const data = await response.json();
const parsedData = parseStringify(data);
- revalidatePath("/providers-overview");
+ revalidatePath("/");
return parsedData;
} catch (error) {
// eslint-disable-next-line no-console
@@ -47,3 +47,97 @@ export const getProvidersOverview = async ({
return undefined;
}
};
+
+export const getFindingsByStatus = async ({
+ page = 1,
+ query = "",
+ sort = "",
+ filters = {},
+}) => {
+ const session = await auth();
+
+ if (isNaN(Number(page)) || page < 1) redirect("/");
+
+ const keyServer = process.env.API_BASE_URL;
+ const url = new URL(`${keyServer}/overviews/findings`);
+
+ if (page) url.searchParams.append("page[number]", page.toString());
+ if (query) url.searchParams.append("filter[search]", query);
+ if (sort) url.searchParams.append("sort", sort);
+
+ // Handle multiple filters
+ Object.entries(filters).forEach(([key, value]) => {
+ if (key !== "filter[search]") {
+ url.searchParams.append(key, String(value));
+ }
+ });
+
+ try {
+ const response = await fetch(url.toString(), {
+ headers: {
+ Accept: "application/vnd.api+json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch findings severity: ${response.status}`);
+ }
+
+ const data = await response.json();
+ const parsedData = parseStringify(data);
+ revalidatePath("/");
+ return parsedData;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("Error fetching findings severity overview:", error);
+ return undefined;
+ }
+};
+
+export const getFindingsBySeverity = async ({
+ page = 1,
+ query = "",
+ sort = "",
+ filters = {},
+}) => {
+ const session = await auth();
+
+ if (isNaN(Number(page)) || page < 1) redirect("/");
+
+ const keyServer = process.env.API_BASE_URL;
+ const url = new URL(`${keyServer}/overviews/findings_severity`);
+
+ if (page) url.searchParams.append("page[number]", page.toString());
+ if (query) url.searchParams.append("filter[search]", query);
+ if (sort) url.searchParams.append("sort", sort);
+
+ // Handle multiple filters
+ Object.entries(filters).forEach(([key, value]) => {
+ if (key !== "filter[search]") {
+ url.searchParams.append(key, String(value));
+ }
+ });
+
+ try {
+ const response = await fetch(url.toString(), {
+ headers: {
+ Accept: "application/vnd.api+json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch findings severity: ${response.status}`);
+ }
+
+ const data = await response.json();
+ const parsedData = parseStringify(data);
+ revalidatePath("/");
+ return parsedData;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("Error fetching findings severity overview:", error);
+ return undefined;
+ }
+};
diff --git a/ui/actions/providers/providers.ts b/ui/actions/providers/providers.ts
index fc5cd97bb7..b11841de40 100644
--- a/ui/actions/providers/providers.ts
+++ b/ui/actions/providers/providers.ts
@@ -102,6 +102,7 @@ export const updateProvider = async (formData: FormData) => {
revalidatePath("/providers");
return parseStringify(data);
} catch (error) {
+ // eslint-disable-next-line no-console
console.error(error);
return {
error: getErrorMessage(error),
@@ -113,13 +114,24 @@ export const addProvider = async (formData: FormData) => {
const session = await auth();
const keyServer = process.env.API_BASE_URL;
- const providerType = formData.get("providerType");
- const providerUid = formData.get("providerUid");
- const providerAlias = formData.get("providerAlias");
+ const providerType = formData.get("providerType") as string;
+ const providerUid = formData.get("providerUid") as string;
+ const providerAlias = formData.get("providerAlias") as string;
const url = new URL(`${keyServer}/providers`);
try {
+ const bodyData = {
+ data: {
+ type: "providers",
+ attributes: {
+ provider: providerType,
+ uid: providerUid,
+ ...(providerAlias?.trim() && { alias: providerAlias.trim() }),
+ },
+ },
+ };
+
const response = await fetch(url.toString(), {
method: "POST",
headers: {
@@ -127,21 +139,14 @@ export const addProvider = async (formData: FormData) => {
Accept: "application/vnd.api+json",
Authorization: `Bearer ${session?.accessToken}`,
},
- body: JSON.stringify({
- data: {
- type: "providers",
- attributes: {
- provider: providerType,
- uid: providerUid,
- alias: providerAlias,
- },
- },
- }),
+ body: JSON.stringify(bodyData),
});
+
const data = await response.json();
revalidatePath("/providers");
return parseStringify(data);
} catch (error) {
+ // eslint-disable-next-line no-console
console.error(error);
return {
error: getErrorMessage(error),
@@ -240,6 +245,103 @@ export const addCredentialsProvider = async (formData: FormData) => {
revalidatePath("/providers");
return parseStringify(data);
} catch (error) {
+ // eslint-disable-next-line no-console
+ console.error(error);
+ return {
+ error: getErrorMessage(error),
+ };
+ }
+};
+
+export const updateCredentialsProvider = async (
+ credentialsId: string,
+ formData: FormData,
+) => {
+ const session = await auth();
+ const keyServer = process.env.API_BASE_URL;
+ const url = new URL(`${keyServer}/providers/secrets/${credentialsId}`);
+
+ const secretName = formData.get("secretName");
+ const providerType = formData.get("providerType");
+
+ const isRole = formData.get("role_arn") !== null;
+
+ let secret = {};
+
+ if (providerType === "aws") {
+ if (isRole) {
+ // Role-based configuration for AWS
+ secret = {
+ role_arn: formData.get("role_arn"),
+ aws_access_key_id: formData.get("aws_access_key_id") || undefined,
+ aws_secret_access_key:
+ formData.get("aws_secret_access_key") || undefined,
+ aws_session_token: formData.get("aws_session_token") || undefined,
+ session_duration:
+ parseInt(formData.get("session_duration") as string, 10) || 3600,
+ external_id: formData.get("external_id") || undefined,
+ role_session_name: formData.get("role_session_name") || undefined,
+ };
+ } else {
+ // Static credentials configuration for AWS
+ secret = {
+ aws_access_key_id: formData.get("aws_access_key_id"),
+ aws_secret_access_key: formData.get("aws_secret_access_key"),
+ aws_session_token: formData.get("aws_session_token") || undefined,
+ };
+ }
+ } else if (providerType === "azure") {
+ // Static credentials configuration for Azure
+ secret = {
+ client_id: formData.get("client_id"),
+ client_secret: formData.get("client_secret"),
+ tenant_id: formData.get("tenant_id"),
+ };
+ } else if (providerType === "gcp") {
+ // Static credentials configuration for GCP
+ secret = {
+ client_id: formData.get("client_id"),
+ client_secret: formData.get("client_secret"),
+ refresh_token: formData.get("refresh_token"),
+ };
+ } else if (providerType === "kubernetes") {
+ // Static credentials configuration for Kubernetes
+ secret = {
+ kubeconfig_content: formData.get("kubeconfig_content"),
+ };
+ }
+
+ const bodyData = {
+ data: {
+ type: "provider-secrets",
+ id: credentialsId,
+ attributes: {
+ name: secretName,
+ secret,
+ },
+ },
+ };
+
+ try {
+ const response = await fetch(url.toString(), {
+ method: "PATCH",
+ headers: {
+ "Content-Type": "application/vnd.api+json",
+ Accept: "application/vnd.api+json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ body: JSON.stringify(bodyData),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to update credentials: ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ revalidatePath("/providers");
+ return parseStringify(data);
+ } catch (error) {
+ // eslint-disable-next-line no-console
console.error(error);
return {
error: getErrorMessage(error),
@@ -264,7 +366,7 @@ export const checkConnectionProvider = async (formData: FormData) => {
},
});
const data = await response.json();
- await wait(1000);
+ await wait(2000);
revalidatePath("/providers");
return parseStringify(data);
} catch (error) {
diff --git a/ui/actions/scans/scans.ts b/ui/actions/scans/scans.ts
index 367446daaa..3ac7d897d6 100644
--- a/ui/actions/scans/scans.ts
+++ b/ui/actions/scans/scans.ts
@@ -110,6 +110,49 @@ export const scanOnDemand = async (formData: FormData) => {
revalidatePath("/scans");
return parseStringify(data);
} catch (error) {
+ // eslint-disable-next-line no-console
+ console.error(error);
+ return {
+ error: getErrorMessage(error),
+ };
+ }
+};
+
+export const scheduleDaily = async (formData: FormData) => {
+ const session = await auth();
+ const keyServer = process.env.API_BASE_URL;
+
+ const providerId = formData.get("providerId");
+
+ const url = new URL(`${keyServer}/schedules/daily`);
+
+ try {
+ const response = await fetch(url.toString(), {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/vnd.api+json",
+ Accept: "application/vnd.api+json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ body: JSON.stringify({
+ data: {
+ type: "daily-schedules",
+ attributes: {
+ provider_id: providerId,
+ },
+ },
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to schedule daily: ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ revalidatePath("/scans");
+ return parseStringify(data);
+ } catch (error) {
+ // eslint-disable-next-line no-console
console.error(error);
return {
error: getErrorMessage(error),
@@ -148,6 +191,7 @@ export const updateScan = async (formData: FormData) => {
revalidatePath("/scans");
return parseStringify(data);
} catch (error) {
+ // eslint-disable-next-line no-console
console.error(error);
return {
error: getErrorMessage(error),
diff --git a/ui/actions/users/users.ts b/ui/actions/users/users.ts
index 30a5b26d1f..cf3397a940 100644
--- a/ui/actions/users/users.ts
+++ b/ui/actions/users/users.ts
@@ -114,3 +114,31 @@ export const deleteUser = async (formData: FormData) => {
};
}
};
+
+export const getProfileInfo = async () => {
+ const session = await auth();
+ const keyServer = process.env.API_BASE_URL;
+ const url = new URL(`${keyServer}/users/me`);
+
+ try {
+ const response = await fetch(url.toString(), {
+ method: "GET",
+ headers: {
+ Accept: "application/vnd.api+json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch user data: ${response.statusText}`);
+ }
+
+ const data = await response.json();
+ const parsedData = parseStringify(data);
+ revalidatePath("/profile");
+ return parsedData;
+ } catch (error) {
+ console.error("Error fetching profile:", error);
+ return undefined;
+ }
+};
diff --git a/ui/app/(prowler)/compliance/page.tsx b/ui/app/(prowler)/compliance/page.tsx
index 38c9800467..558caeaf9b 100644
--- a/ui/app/(prowler)/compliance/page.tsx
+++ b/ui/app/(prowler)/compliance/page.tsx
@@ -16,44 +16,70 @@ export default async function Compliance({
}: {
searchParams: SearchParamsProps;
}) {
- const scansData = await getScans({});
- const scanList = scansData?.data
- .filter(
- (scan: any) =>
- scan.attributes.state === "completed" &&
- scan.attributes.progress === 100,
- )
- .map((scan: any) => ({
- id: scan.id,
- name: scan.attributes.name || "Unnamed Scan",
- state: scan.attributes.state,
- progress: scan.attributes.progress,
- }));
+ let scansData;
+ let scanList: {
+ id: string;
+ name: string;
+ state: string;
+ progress: number;
+ }[] = [];
- const selectedScanId = searchParams.scanId || scanList[0]?.id;
+ try {
+ scansData = await getScans({});
+ scanList =
+ scansData?.data
+ ?.filter(
+ (scan: any) =>
+ scan.attributes.state === "completed" &&
+ scan.attributes.progress === 100,
+ )
+ .map((scan: any) => ({
+ id: scan.id,
+ name: scan.attributes.name || "Unnamed Scan",
+ state: scan.attributes.state,
+ progress: scan.attributes.progress,
+ })) || [];
+ } catch (error) {
+ console.error("Error fetching scans data:", error);
+ }
+
+ const selectedScanId = searchParams.scanId || scanList[0]?.id || null;
+
+ // If there are no scans available, return a message
+ if (!selectedScanId) {
+ return (
+ There is no providers overview info available
; - } - - return{JSON.stringify(session.user, null, 2)}
- {JSON.stringify(session.userId, null, 2)}
- {JSON.stringify(session.tenantId, null, 2)}
- {JSON.stringify(session, null, 2)}
+ Need to create an account? diff --git a/ui/components/charts/SeverityChart.tsx b/ui/components/charts/SeverityChart.tsx deleted file mode 100644 index 4c3060c8bd..0000000000 --- a/ui/components/charts/SeverityChart.tsx +++ /dev/null @@ -1,76 +0,0 @@ -"use client"; - -import { Bar, BarChart, LabelList, XAxis, YAxis } from "recharts"; - -import { - ChartConfig, - ChartContainer, - ChartTooltip, - ChartTooltipContent, -} from "@/components/ui/chart/Chart"; - -const chartData = [ - { severity: "critical", findings: 32, fill: "var(--color-critical)" }, - { severity: "high", findings: 78, fill: "var(--color-high)" }, - { severity: "medium", findings: 117, fill: "var(--color-medium)" }, - { severity: "low", findings: 39, fill: "var(--color-low)" }, -]; - -const chartConfig = { - findings: { - label: "Findings", - }, - critical: { - label: "Critical", - color: "hsl(var(--chart-critical))", - }, - high: { - label: "High", - color: "hsl(var(--chart-fail))", - }, - medium: { - label: "Medium", - color: "hsl(var(--chart-medium))", - }, - low: { - label: "Low", - color: "hsl(var(--chart-low))", - }, -} satisfies ChartConfig; - -export const SeverityChart = () => { - return ( -