mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-04 02:04:06 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39e903ade3 | ||
|
|
477f3ebda1 | ||
|
|
3ca189a52a | ||
|
|
4014fcb6c1 |
@@ -30,6 +30,7 @@ jobs:
|
||||
- '3.11'
|
||||
- '3.12'
|
||||
- '3.13'
|
||||
- '3.14'
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
|
||||
@@ -102,6 +102,7 @@ jobs:
|
||||
- '3.11'
|
||||
- '3.12'
|
||||
- '3.13'
|
||||
- '3.14'
|
||||
package:
|
||||
- 'prowler'
|
||||
include:
|
||||
@@ -119,6 +120,8 @@ jobs:
|
||||
github.com:443
|
||||
api.github.com:443
|
||||
release-assets.githubusercontent.com:443
|
||||
results-receiver.actions.githubusercontent.com:443
|
||||
*.blob.core.windows.net:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
|
||||
@@ -145,6 +148,15 @@ jobs:
|
||||
- name: Check metadata with the release workflow's twine
|
||||
run: uvx --from "twine==${TWINE_VERSION}" twine check --strict dist/*
|
||||
|
||||
- name: Upload Prowler wheel for portability checks
|
||||
if: matrix.python-version == '3.14' && matrix.package == 'prowler'
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: prowler-python-3.14-wheel
|
||||
path: dist/prowler-*.whl
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- name: Install the wheel with pip into a clean virtualenv
|
||||
# Plain pip, --isolated, from outside the repo: consumers never see [tool.uv]
|
||||
# override-dependencies or constraint-dependencies, so neither does this step.
|
||||
@@ -162,6 +174,85 @@ jobs:
|
||||
# from the package. grep fails the step if the summary line never appears.
|
||||
"${RUNNER_TEMP}/consumer/bin/prowler" aws --list-checks | grep 'available checks'
|
||||
|
||||
python-3-14-binary-wheel-portability:
|
||||
needs: install-from-wheel
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runner:
|
||||
- ubuntu-latest
|
||||
- macos-15-intel
|
||||
- macos-15
|
||||
- windows-latest
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
||||
with:
|
||||
# Block mode is unavailable on GitHub-hosted macOS and Windows runners.
|
||||
egress-policy: audit
|
||||
|
||||
- name: Set up Python 3.14
|
||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
with:
|
||||
python-version: '3.14'
|
||||
|
||||
- name: Download built Prowler wheel
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: prowler-python-3.14-wheel
|
||||
path: dist
|
||||
|
||||
- name: Extract NumPy and pandas requirements from wheel metadata
|
||||
shell: python
|
||||
run: |
|
||||
from email.parser import BytesParser
|
||||
from pathlib import Path
|
||||
import re
|
||||
from zipfile import ZipFile
|
||||
|
||||
wheel = next(Path("dist").glob("prowler-*.whl"))
|
||||
with ZipFile(wheel) as archive:
|
||||
metadata_name = next(
|
||||
name
|
||||
for name in archive.namelist()
|
||||
if name.endswith(".dist-info/METADATA")
|
||||
)
|
||||
metadata = BytesParser().parsebytes(archive.read(metadata_name))
|
||||
|
||||
requirements = [
|
||||
requirement
|
||||
for requirement in metadata.get_all("Requires-Dist", [])
|
||||
if re.match(r"^(numpy|pandas)(?:\W|$)", requirement, re.IGNORECASE)
|
||||
]
|
||||
requirement_names = {
|
||||
re.match(r"^(numpy|pandas)", requirement, re.IGNORECASE).group(1).lower()
|
||||
for requirement in requirements
|
||||
}
|
||||
if requirement_names != {"numpy", "pandas"}:
|
||||
raise SystemExit(
|
||||
f"Expected NumPy and pandas requirements, found: {requirements}"
|
||||
)
|
||||
|
||||
Path("binary-wheel-requirements.txt").write_text(
|
||||
"\n".join(requirements) + "\n", encoding="utf-8"
|
||||
)
|
||||
print("Wheel requirements:", *requirements, sep="\n ")
|
||||
|
||||
# huaweicloudsdkcore hard-pins pymongo 4.15.1, which has no cp314 wheel;
|
||||
# its optional C extensions fall back to a pure-Python build without a compiler.
|
||||
- name: Require binary distributions for marked NumPy and pandas versions
|
||||
run: >-
|
||||
python -m pip download
|
||||
--only-binary=:all:
|
||||
--dest binary-wheels
|
||||
--requirement binary-wheel-requirements.txt
|
||||
|
||||
pinned-releases-not-yanked:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.run == 'true'
|
||||
|
||||
@@ -30,6 +30,7 @@ jobs:
|
||||
- '3.11'
|
||||
- '3.12'
|
||||
- '3.13'
|
||||
- '3.14'
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
|
||||
@@ -285,7 +285,7 @@ Some pre-commit hooks require tools installed on your system:
|
||||
|
||||
## Prowler CLI
|
||||
### Pip package
|
||||
Prowler CLI is available as a project in [PyPI](https://pypi.org/project/prowler-cloud/). Consequently, it can be installed using pip with Python >=3.10, <3.13:
|
||||
Prowler CLI is available as a project in [PyPI](https://pypi.org/project/prowler-cloud/). Consequently, it can be installed using pip with Python >=3.10, <3.15:
|
||||
|
||||
```console
|
||||
pip install prowler
|
||||
@@ -317,7 +317,7 @@ The container images are available here:
|
||||
|
||||
### From GitHub
|
||||
|
||||
Python >=3.10, <3.13 is required with [uv](https://docs.astral.sh/uv/):
|
||||
Python >=3.10, <3.15 is required with [uv](https://docs.astral.sh/uv/):
|
||||
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Support for Python 3.14
|
||||
+13
-11
@@ -12,7 +12,7 @@ dev = [
|
||||
"flake8==7.1.2",
|
||||
"freezegun==1.5.1",
|
||||
"mock==5.2.0",
|
||||
"moto[all]==5.1.11",
|
||||
"moto[all]==5.2.3",
|
||||
"openapi-schema-validator==0.6.3",
|
||||
"openapi-spec-validator==0.7.1",
|
||||
"prek==0.3.9",
|
||||
@@ -33,7 +33,8 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13"
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14"
|
||||
]
|
||||
dependencies = [
|
||||
"alive-progress==3.3.0",
|
||||
@@ -85,13 +86,15 @@ dependencies = [
|
||||
"linode-api4==5.45.0",
|
||||
"markdown==3.10.2",
|
||||
"microsoft-kiota-abstractions==1.9.10",
|
||||
"numpy==2.2.6",
|
||||
"numpy==2.2.6 ; python_version < '3.14'",
|
||||
"numpy==2.3.2 ; python_version >= '3.14'",
|
||||
"msgraph-sdk==1.55.0",
|
||||
"okta==3.4.2",
|
||||
"openstacksdk==4.2.0",
|
||||
"pandas==2.2.3",
|
||||
"pandas==2.2.3 ; python_version < '3.14'",
|
||||
"pandas==2.3.3 ; python_version >= '3.14'",
|
||||
"py-ocsf-models==0.10.0",
|
||||
"pydantic==2.12.5",
|
||||
"pydantic==2.13.5",
|
||||
"pygithub==2.8.0",
|
||||
"python-dateutil==2.9.0.post0",
|
||||
"pytz==2025.1",
|
||||
@@ -142,7 +145,7 @@ license = "Apache-2.0"
|
||||
maintainers = [{name = "Prowler Engineering", email = "engineering@prowler.com"}]
|
||||
name = "prowler"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10,<3.14"
|
||||
requires-python = ">=3.10,<3.15"
|
||||
version = "5.41.0"
|
||||
|
||||
[project.scripts]
|
||||
@@ -218,7 +221,6 @@ constraint-dependencies = [
|
||||
"astroid==3.3.11",
|
||||
"async-timeout==5.0.1",
|
||||
"attrs==26.1.0",
|
||||
"aws-sam-translator==1.109.0",
|
||||
"aws-xray-sdk==2.15.0",
|
||||
"azure-common==1.1.28",
|
||||
"azure-core==1.41.0",
|
||||
@@ -228,7 +230,7 @@ constraint-dependencies = [
|
||||
"blinker==1.9.0",
|
||||
"certifi==2026.4.22",
|
||||
"cffi==2.0.0",
|
||||
"cfn-lint==1.51.0",
|
||||
"cfn-lint==1.55.1",
|
||||
"charset-normalizer==3.4.7",
|
||||
"circuitbreaker==2.1.3",
|
||||
"click==8.3.3",
|
||||
@@ -305,7 +307,7 @@ constraint-dependencies = [
|
||||
"microsoft-kiota-serialization-multipart==1.9.10",
|
||||
"microsoft-kiota-serialization-text==1.9.10",
|
||||
"mock==5.2.0",
|
||||
"moto==5.1.11",
|
||||
"moto==5.2.3",
|
||||
"mpmath==1.3.0",
|
||||
"msal==1.37.0",
|
||||
"msal-extensions==1.3.1",
|
||||
@@ -337,13 +339,13 @@ constraint-dependencies = [
|
||||
"proto-plus==1.28.0",
|
||||
"protobuf==7.34.1",
|
||||
"psutil==7.2.2",
|
||||
"py-partiql-parser==0.6.1",
|
||||
"py-partiql-parser==0.6.3",
|
||||
"pyasn1==0.6.4",
|
||||
"pyasn1-modules==0.4.2",
|
||||
"pycodestyle==2.12.1",
|
||||
"pycparser==3.0",
|
||||
"pycryptodomex==3.23.0",
|
||||
"pydantic-core==2.41.5",
|
||||
"pydantic-core==2.46.5",
|
||||
"pydash==8.0.6",
|
||||
"pyflakes==3.2.0",
|
||||
"pygments==2.20.0",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
from freezegun import configure
|
||||
|
||||
# Skip lazy Okta imports building Pydantic schemas against frozen datetime.
|
||||
configure(extend_ignore_list=["okta"])
|
||||
@@ -0,0 +1,17 @@
|
||||
import datetime
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
|
||||
def test_freezegun_handles_lazy_okta_models_and_restores_datetime():
|
||||
import okta.client # noqa: F401
|
||||
|
||||
real_datetime_id = id(datetime.datetime)
|
||||
|
||||
with freeze_time("2025-01-01 00:00:00"):
|
||||
from okta.models import NetworkZoneAddress
|
||||
|
||||
assert NetworkZoneAddress.__name__ == "NetworkZoneAddress"
|
||||
assert datetime.datetime.now() == datetime.datetime(2025, 1, 1)
|
||||
|
||||
assert id(datetime.datetime) == real_datetime_id
|
||||
@@ -184,6 +184,6 @@ class TestM365CIS:
|
||||
|
||||
mock_file.seek(0)
|
||||
content = mock_file.read()
|
||||
expected_csv = f"PROVIDER;DESCRIPTION;TENANTID;LOCATION;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_SUBSECTION;REQUIREMENTS_ATTRIBUTES_PROFILE;REQUIREMENTS_ATTRIBUTES_ASSESSMENTSTATUS;REQUIREMENTS_ATTRIBUTES_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_RATIONALESTATEMENT;REQUIREMENTS_ATTRIBUTES_IMPACTSTATEMENT;REQUIREMENTS_ATTRIBUTES_REMEDIATIONPROCEDURE;REQUIREMENTS_ATTRIBUTES_AUDITPROCEDURE;REQUIREMENTS_ATTRIBUTES_ADDITIONALINFORMATION;REQUIREMENTS_ATTRIBUTES_DEFAULTVALUE;REQUIREMENTS_ATTRIBUTES_REFERENCES;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED;FRAMEWORK;NAME\r\nm365;The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for configuring security options for Microsoft 365 with an emphasis on foundational, testable, and architecture agnostic settings.;00000000-0000-0000-0000-000000000000;global;{datetime.now()};2.1.3;Ensure MFA Delete is enabled on S3 buckets;2.1. Simple Storage Service (S3);;Level 1;Automated;Once MFA Delete is enabled on your sensitive and classified S3 bucket it requires the user to have two forms of authentication.;Adding MFA delete to an S3 bucket, requires additional authentication when you change the version state of your bucket or you delete and object version adding another layer of security in the event your security credentials are compromised or unauthorized access is granted.;;Perform the steps below to enable MFA delete on an S3 bucket.Note:-You cannot enable MFA Delete using the AWS Management Console. You must use the AWS CLI or API.-You must use your 'root' account to enable MFA Delete on S3 buckets.**From Command line:**1. Run the s3api put-bucket-versioning command aws s3api put-bucket-versioning --profile my-root-profile --bucket Bucket_Name --versioning-configuration Status=Enabled,MFADelete=Enabled --mfa arn:aws:iam::aws_account_id:mfa/root-account-mfa-device passcode;Perform the steps below to confirm MFA delete is configured on an S3 Bucket**From Console:**1. Login to the S3 console at `https://console.aws.amazon.com/s3/`2. Click the `Check` box next to the Bucket name you want to confirm3. In the window under `Properties`4. Confirm that Versioning is `Enabled`5. Confirm that MFA Delete is `Enabled`**From Command Line:**1. Run the `get-bucket-versioning aws s3api get-bucket-versioning --bucket my-bucket Output example: <VersioningConfiguration xmlns=`http://s3.amazonaws.com/doc/2006-03-01/`> <Status>Enabled</Status> <MfaDelete>Enabled</MfaDelete></VersioningConfiguration>\ If the Console or the CLI output does not show Versioning and MFA Delete `enabled` refer to the remediation below.;;By default, MFA Delete is not enabled on S3 buckets.;https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html#MultiFactorAuthenticationDelete:https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMFADelete.html:https://aws.amazon.com/blogs/security/securing-access-to-aws-using-mfa-part-3/:https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_lost-or-broken.html;PASS;;;;service_test_check_id;False;CIS;CIS Microsoft 365 Foundations Benchmark v4.0.0\r\nm365;The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for configuring security options for Microsoft 365 with an emphasis on foundational, testable, and architecture agnostic settings.;;;{datetime.now()};2.1.4;Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive;1.1 Control Plane Node Configuration Files;;Level 1;Automated;Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.;The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.;;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml ```;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml ``` Verify that the permissions are `600` or more restrictive.;;By default, the `kube-controller-manager.yaml` file has permissions of `640`.;https://kubernetes.io/docs/admin/kube-apiserver/;MANUAL;Manual check;manual_check;Manual check;manual;False;CIS;CIS Microsoft 365 Foundations Benchmark v4.0.0\r\n"
|
||||
expected_csv = f"PROVIDER;DESCRIPTION;TENANTID;LOCATION;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_SUBSECTION;REQUIREMENTS_ATTRIBUTES_PROFILE;REQUIREMENTS_ATTRIBUTES_ASSESSMENTSTATUS;REQUIREMENTS_ATTRIBUTES_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_RATIONALESTATEMENT;REQUIREMENTS_ATTRIBUTES_IMPACTSTATEMENT;REQUIREMENTS_ATTRIBUTES_REMEDIATIONPROCEDURE;REQUIREMENTS_ATTRIBUTES_AUDITPROCEDURE;REQUIREMENTS_ATTRIBUTES_ADDITIONALINFORMATION;REQUIREMENTS_ATTRIBUTES_DEFAULTVALUE;REQUIREMENTS_ATTRIBUTES_REFERENCES;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED;FRAMEWORK;NAME\r\nm365;The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for configuring security options for Microsoft 365 with an emphasis on foundational, testable, and architecture agnostic settings.;00000000-0000-0000-0000-000000000000;global;{datetime.now()};2.1.3;Ensure MFA Delete is enabled on S3 buckets;2.1. Simple Storage Service (S3);;Level 1;Automated;Once MFA Delete is enabled on your sensitive and classified S3 bucket it requires the user to have two forms of authentication.;Adding MFA delete to an S3 bucket, requires additional authentication when you change the version state of your bucket or you delete and object version adding another layer of security in the event your security credentials are compromised or unauthorized access is granted.;;Perform the steps below to enable MFA delete on an S3 bucket.Note:-You cannot enable MFA Delete using the AWS Management Console. You must use the AWS CLI or API.-You must use your 'root' account to enable MFA Delete on S3 buckets.**From Command line:**1. Run the s3api put-bucket-versioning command aws s3api put-bucket-versioning --profile my-root-profile --bucket Bucket_Name --versioning-configuration Status=Enabled,MFADelete=Enabled --mfa arn:aws:iam::aws_account_id:mfa/root-account-mfa-device passcode;Perform the steps below to confirm MFA delete is configured on an S3 Bucket**From Console:**1. Login to the S3 console at `https://console.aws.amazon.com/s3/`2. Click the `Check` box next to the Bucket name you want to confirm3. In the window under `Properties`4. Confirm that Versioning is `Enabled`5. Confirm that MFA Delete is `Enabled`**From Command Line:**1. Run the `get-bucket-versioning aws s3api get-bucket-versioning --bucket my-bucket Output example: <VersioningConfiguration xmlns=`http://s3.amazonaws.com/doc/2006-03-01/`> <Status>Enabled</Status> <MfaDelete>Enabled</MfaDelete></VersioningConfiguration>\\ If the Console or the CLI output does not show Versioning and MFA Delete `enabled` refer to the remediation below.;;By default, MFA Delete is not enabled on S3 buckets.;https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html#MultiFactorAuthenticationDelete:https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMFADelete.html:https://aws.amazon.com/blogs/security/securing-access-to-aws-using-mfa-part-3/:https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_lost-or-broken.html;PASS;;;;service_test_check_id;False;CIS;CIS Microsoft 365 Foundations Benchmark v4.0.0\r\nm365;The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for configuring security options for Microsoft 365 with an emphasis on foundational, testable, and architecture agnostic settings.;;;{datetime.now()};2.1.4;Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive;1.1 Control Plane Node Configuration Files;;Level 1;Automated;Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.;The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.;;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml ```;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml ``` Verify that the permissions are `600` or more restrictive.;;By default, the `kube-controller-manager.yaml` file has permissions of `640`.;https://kubernetes.io/docs/admin/kube-apiserver/;MANUAL;Manual check;manual_check;Manual check;manual;False;CIS;CIS Microsoft 365 Foundations Benchmark v4.0.0\r\n"
|
||||
|
||||
assert content == expected_csv
|
||||
|
||||
@@ -273,7 +273,7 @@ CIS_4_0_M365 = Compliance(
|
||||
RationaleStatement="Adding MFA delete to an S3 bucket, requires additional authentication when you change the version state of your bucket or you delete and object version adding another layer of security in the event your security credentials are compromised or unauthorized access is granted.",
|
||||
ImpactStatement="",
|
||||
RemediationProcedure="Perform the steps below to enable MFA delete on an S3 bucket.Note:-You cannot enable MFA Delete using the AWS Management Console. You must use the AWS CLI or API.-You must use your 'root' account to enable MFA Delete on S3 buckets.**From Command line:**1. Run the s3api put-bucket-versioning command aws s3api put-bucket-versioning --profile my-root-profile --bucket Bucket_Name --versioning-configuration Status=Enabled,MFADelete=Enabled --mfa arn:aws:iam::aws_account_id:mfa/root-account-mfa-device passcode",
|
||||
AuditProcedure="Perform the steps below to confirm MFA delete is configured on an S3 Bucket**From Console:**1. Login to the S3 console at `https://console.aws.amazon.com/s3/`2. Click the `Check` box next to the Bucket name you want to confirm3. In the window under `Properties`4. Confirm that Versioning is `Enabled`5. Confirm that MFA Delete is `Enabled`**From Command Line:**1. Run the `get-bucket-versioning aws s3api get-bucket-versioning --bucket my-bucket Output example: <VersioningConfiguration xmlns=`http://s3.amazonaws.com/doc/2006-03-01/`> <Status>Enabled</Status> <MfaDelete>Enabled</MfaDelete></VersioningConfiguration>\ If the Console or the CLI output does not show Versioning and MFA Delete `enabled` refer to the remediation below.",
|
||||
AuditProcedure=r"Perform the steps below to confirm MFA delete is configured on an S3 Bucket**From Console:**1. Login to the S3 console at `https://console.aws.amazon.com/s3/`2. Click the `Check` box next to the Bucket name you want to confirm3. In the window under `Properties`4. Confirm that Versioning is `Enabled`5. Confirm that MFA Delete is `Enabled`**From Command Line:**1. Run the `get-bucket-versioning aws s3api get-bucket-versioning --bucket my-bucket Output example: <VersioningConfiguration xmlns=`http://s3.amazonaws.com/doc/2006-03-01/`> <Status>Enabled</Status> <MfaDelete>Enabled</MfaDelete></VersioningConfiguration>\ If the Console or the CLI output does not show Versioning and MFA Delete `enabled` refer to the remediation below.",
|
||||
AdditionalInformation="",
|
||||
References="https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html#MultiFactorAuthenticationDelete:https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMFADelete.html:https://aws.amazon.com/blogs/security/securing-access-to-aws-using-mfa-part-3/:https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_lost-or-broken.html",
|
||||
DefaultValue="By default, MFA Delete is not enabled on S3 buckets.",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from datetime import datetime
|
||||
from io import StringIO
|
||||
from unittest import mock
|
||||
|
||||
@@ -6,9 +5,7 @@ from freezegun import freeze_time
|
||||
from mock import patch
|
||||
|
||||
from prowler.lib.check.compliance_config_eval import CONFIG_NOT_VALID_PREFIX
|
||||
from prowler.lib.check.compliance_models import (
|
||||
Compliance_Requirement_ConfigConstraint,
|
||||
)
|
||||
from prowler.lib.check.compliance_models import Compliance_Requirement_ConfigConstraint
|
||||
from prowler.lib.outputs.compliance.okta_idaas_stig.models import OktaIDaaSSTIGModel
|
||||
from prowler.lib.outputs.compliance.okta_idaas_stig.okta_idaas_stig_okta import (
|
||||
OktaIDaaSSTIG,
|
||||
@@ -138,7 +135,7 @@ class TestOktaIDaaSSTIG:
|
||||
|
||||
mock_file.seek(0)
|
||||
content = mock_file.read()
|
||||
expected_csv = f"PROVIDER;DESCRIPTION;ORGANIZATIONDOMAIN;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_NAME;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_SEVERITY;REQUIREMENTS_ATTRIBUTES_RULEID;REQUIREMENTS_ATTRIBUTES_STIGID;REQUIREMENTS_ATTRIBUTES_CCI;REQUIREMENTS_ATTRIBUTES_CHECKTEXT;REQUIREMENTS_ATTRIBUTES_FIXTEXT;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED;FRAMEWORK;NAME\r\nokta;Defense Information Systems Agency (DISA) Security Technical Implementation Guide (STIG) for Okta Identity as a Service (IDaaS).;{OKTA_ORG_DOMAIN};{datetime.now()};OKTA-APP-000020;Okta must log out a session after a 15-minute period of inactivity.;A session timeout lock is a temporary action taken when a user stops work and moves away from the immediate vicinity of the information system.;CAT II (Medium);medium;SV-273186r1098825_rule;OKTA-APP-000020;['CCI-000057', 'CCI-001133'];Verify the Global Session Policy logs out a session after 15 minutes of inactivity.;From the Admin Console configure the Global Session Policy idle timeout to 15 minutes.;PASS;;okta-global-session-policy;Default Policy;signon_global_session_idle_timeout_15min;False;Okta-IDaaS-STIG;DISA Okta Identity as a Service (IDaaS) STIG V1R2\r\nokta;Defense Information Systems Agency (DISA) Security Technical Implementation Guide (STIG) for Okta Identity as a Service (IDaaS).;;{datetime.now()};OKTA-APP-000650;Okta must enforce a minimum 15-character password length.;The shorter the password, the lower the number of possible combinations that need to be tested before the password is compromised.;CAT II (Medium);medium;SV-273209r1098894_rule;OKTA-APP-000650;['CCI-000205'];Verify the password policy enforces a minimum length of 15 characters.;From the Admin Console set the minimum password length to 15 characters.;MANUAL;Manual check;manual_check;Manual check;manual;False;Okta-IDaaS-STIG;DISA Okta Identity as a Service (IDaaS) STIG V1R2\r\n"
|
||||
expected_csv = f"PROVIDER;DESCRIPTION;ORGANIZATIONDOMAIN;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_NAME;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_SEVERITY;REQUIREMENTS_ATTRIBUTES_RULEID;REQUIREMENTS_ATTRIBUTES_STIGID;REQUIREMENTS_ATTRIBUTES_CCI;REQUIREMENTS_ATTRIBUTES_CHECKTEXT;REQUIREMENTS_ATTRIBUTES_FIXTEXT;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED;FRAMEWORK;NAME\r\nokta;Defense Information Systems Agency (DISA) Security Technical Implementation Guide (STIG) for Okta Identity as a Service (IDaaS).;{OKTA_ORG_DOMAIN};2025-01-01 00:00:00;OKTA-APP-000020;Okta must log out a session after a 15-minute period of inactivity.;A session timeout lock is a temporary action taken when a user stops work and moves away from the immediate vicinity of the information system.;CAT II (Medium);medium;SV-273186r1098825_rule;OKTA-APP-000020;['CCI-000057', 'CCI-001133'];Verify the Global Session Policy logs out a session after 15 minutes of inactivity.;From the Admin Console configure the Global Session Policy idle timeout to 15 minutes.;PASS;;okta-global-session-policy;Default Policy;signon_global_session_idle_timeout_15min;False;Okta-IDaaS-STIG;DISA Okta Identity as a Service (IDaaS) STIG V1R2\r\nokta;Defense Information Systems Agency (DISA) Security Technical Implementation Guide (STIG) for Okta Identity as a Service (IDaaS).;;2025-01-01 00:00:00;OKTA-APP-000650;Okta must enforce a minimum 15-character password length.;The shorter the password, the lower the number of possible combinations that need to be tested before the password is compromised.;CAT II (Medium);medium;SV-273209r1098894_rule;OKTA-APP-000650;['CCI-000205'];Verify the password policy enforces a minimum length of 15 characters.;From the Admin Console set the minimum password length to 15 characters.;MANUAL;Manual check;manual_check;Manual check;manual;False;Okta-IDaaS-STIG;DISA Okta Identity as a Service (IDaaS) STIG V1R2\r\n"
|
||||
|
||||
assert content == expected_csv
|
||||
|
||||
|
||||
@@ -1311,11 +1311,11 @@ aws:
|
||||
],
|
||||
)
|
||||
instance_id = instances["Instances"][0]["InstanceId"]
|
||||
instance_arn = f"arn:aws:ec2:{AWS_REGION_EU_CENTRAL_1}:{AWS_ACCOUNT_NUMBER}:ec2:instance/{instance_id}"
|
||||
instance_arn = f"arn:aws:ec2:{AWS_REGION_EU_CENTRAL_1}:{AWS_ACCOUNT_NUMBER}:instance/{instance_id}"
|
||||
image_id = ec2_client.create_image(Name="testami", InstanceId=instance_id)[
|
||||
"ImageId"
|
||||
]
|
||||
image_arn = f"arn:aws:ec2:{AWS_REGION_EU_CENTRAL_1}:{AWS_ACCOUNT_NUMBER}:ec2:image/{image_id}"
|
||||
image_arn = f"arn:aws:ec2:{AWS_REGION_EU_CENTRAL_1}:{AWS_ACCOUNT_NUMBER}:image/{image_id}"
|
||||
ec2_client.create_tags(
|
||||
Resources=[image_id], Tags=[{"Key": "ami", "Value": "test"}]
|
||||
)
|
||||
|
||||
@@ -3,11 +3,11 @@ from json import dumps
|
||||
from os import path
|
||||
|
||||
import botocore
|
||||
import pytest
|
||||
import yaml
|
||||
from boto3 import client, resource
|
||||
from mock import MagicMock, patch
|
||||
from moto import mock_aws
|
||||
import pytest
|
||||
|
||||
from prowler.config.config import encoding_format_utf_8
|
||||
from prowler.providers.aws.lib.mutelist.mutelist import AWSMutelist
|
||||
@@ -1321,7 +1321,7 @@ class TestAWSMutelist:
|
||||
"check_test": {
|
||||
"Regions": [AWS_REGION_US_EAST_1, AWS_REGION_EU_WEST_1],
|
||||
"Resources": ["*"],
|
||||
"Tags": ["environment=dev", "project=test(?!\.)"],
|
||||
"Tags": ["environment=dev", r"project=test(?!\.)"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-34
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client, resource
|
||||
@@ -23,6 +24,7 @@ def mock_generate_regional_clients(provider, service):
|
||||
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
|
||||
new=mock_generate_regional_clients,
|
||||
)
|
||||
@patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "false"})
|
||||
class Test_ec2_ebs_public_snapshot:
|
||||
@mock_aws
|
||||
def test_ec2_default_snapshots(self):
|
||||
@@ -50,8 +52,7 @@ class Test_ec2_ebs_public_snapshot:
|
||||
check = ec2_ebs_public_snapshot()
|
||||
result = check.execute()
|
||||
|
||||
# Default snapshots (moto 5.1.11 creates additional default snapshots)
|
||||
assert len(result) == 565
|
||||
assert result == []
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_public_snapshot(self):
|
||||
@@ -91,22 +92,20 @@ class Test_ec2_ebs_public_snapshot:
|
||||
check = ec2_ebs_public_snapshot()
|
||||
results = check.execute()
|
||||
|
||||
# Default snapshots + 1 created (moto 5.1.11 creates additional default snapshots)
|
||||
assert len(results) == 566
|
||||
|
||||
for snap in results:
|
||||
if snap.resource_id == snapshot.id:
|
||||
assert snap.region == AWS_REGION_US_EAST_1
|
||||
assert snap.resource_tags == []
|
||||
assert snap.status == "FAIL"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is currently Public."
|
||||
)
|
||||
assert (
|
||||
snap.resource_arn
|
||||
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:snapshot/{snapshot.id}"
|
||||
)
|
||||
assert len(results) == 1
|
||||
snap = results[0]
|
||||
assert snap.resource_id == snapshot.id
|
||||
assert snap.region == AWS_REGION_US_EAST_1
|
||||
assert snap.resource_tags == []
|
||||
assert snap.status == "FAIL"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is currently Public."
|
||||
)
|
||||
assert (
|
||||
snap.resource_arn
|
||||
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:snapshot/{snapshot.id}"
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_private_snapshot(self):
|
||||
@@ -141,19 +140,14 @@ class Test_ec2_ebs_public_snapshot:
|
||||
check = ec2_ebs_public_snapshot()
|
||||
results = check.execute()
|
||||
|
||||
# Default snapshots + 1 created (moto 5.1.11 creates additional default snapshots)
|
||||
assert len(results) == 566
|
||||
|
||||
for snap in results:
|
||||
if snap.resource_id == snapshot.id:
|
||||
assert snap.region == AWS_REGION_US_EAST_1
|
||||
assert snap.resource_tags == []
|
||||
assert snap.status == "PASS"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is not Public."
|
||||
)
|
||||
assert (
|
||||
snap.resource_arn
|
||||
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:snapshot/{snapshot.id}"
|
||||
)
|
||||
assert len(results) == 1
|
||||
snap = results[0]
|
||||
assert snap.resource_id == snapshot.id
|
||||
assert snap.region == AWS_REGION_US_EAST_1
|
||||
assert snap.resource_tags == []
|
||||
assert snap.status == "PASS"
|
||||
assert snap.status_extended == f"EBS Snapshot {snapshot.id} is not Public."
|
||||
assert (
|
||||
snap.resource_arn
|
||||
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:snapshot/{snapshot.id}"
|
||||
)
|
||||
|
||||
+25
-34
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import resource
|
||||
@@ -23,6 +24,7 @@ def mock_generate_regional_clients(provider, service):
|
||||
"prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients",
|
||||
new=mock_generate_regional_clients,
|
||||
)
|
||||
@patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "false"})
|
||||
class Test_ec2_ebs_snapshots_encrypted:
|
||||
@mock_aws
|
||||
def test_ec2_default_snapshots(self):
|
||||
@@ -50,8 +52,7 @@ class Test_ec2_ebs_snapshots_encrypted:
|
||||
check = ec2_ebs_snapshots_encrypted()
|
||||
result = check.execute()
|
||||
|
||||
# Default snapshots (moto 5.1.11 creates additional default snapshots)
|
||||
assert len(result) == 565
|
||||
assert result == []
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_unencrypted_snapshot(self):
|
||||
@@ -84,22 +85,17 @@ class Test_ec2_ebs_snapshots_encrypted:
|
||||
check = ec2_ebs_snapshots_encrypted()
|
||||
results = check.execute()
|
||||
|
||||
# Default snapshots + 1 created (moto 5.1.11 creates additional default snapshots)
|
||||
assert len(results) == 566
|
||||
|
||||
for snap in results:
|
||||
if snap.resource_id == snapshot.id:
|
||||
assert snap.region == AWS_REGION_US_EAST_1
|
||||
assert snap.resource_tags == []
|
||||
assert snap.status == "FAIL"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is unencrypted."
|
||||
)
|
||||
assert (
|
||||
snap.resource_arn
|
||||
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:snapshot/{snapshot.id}"
|
||||
)
|
||||
assert len(results) == 1
|
||||
snap = results[0]
|
||||
assert snap.resource_id == snapshot.id
|
||||
assert snap.region == AWS_REGION_US_EAST_1
|
||||
assert snap.resource_tags == []
|
||||
assert snap.status == "FAIL"
|
||||
assert snap.status_extended == f"EBS Snapshot {snapshot.id} is unencrypted."
|
||||
assert (
|
||||
snap.resource_arn
|
||||
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:snapshot/{snapshot.id}"
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_ec2_encrypted_snapshot(self):
|
||||
@@ -134,19 +130,14 @@ class Test_ec2_ebs_snapshots_encrypted:
|
||||
check = ec2_ebs_snapshots_encrypted()
|
||||
results = check.execute()
|
||||
|
||||
# Default snapshots + 1 created (moto 5.1.11 creates additional default snapshots)
|
||||
assert len(results) == 566
|
||||
|
||||
for snap in results:
|
||||
if snap.resource_id == snapshot.id:
|
||||
assert snap.region == AWS_REGION_US_EAST_1
|
||||
assert snap.resource_tags == []
|
||||
assert snap.status == "PASS"
|
||||
assert (
|
||||
snap.status_extended
|
||||
== f"EBS Snapshot {snapshot.id} is encrypted."
|
||||
)
|
||||
assert (
|
||||
snap.resource_arn
|
||||
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:snapshot/{snapshot.id}"
|
||||
)
|
||||
assert len(results) == 1
|
||||
snap = results[0]
|
||||
assert snap.resource_id == snapshot.id
|
||||
assert snap.region == AWS_REGION_US_EAST_1
|
||||
assert snap.resource_tags == []
|
||||
assert snap.status == "PASS"
|
||||
assert snap.status_extended == f"EBS Snapshot {snapshot.id} is encrypted."
|
||||
assert (
|
||||
snap.resource_arn
|
||||
== f"arn:{aws_provider.identity.partition}:ec2:{AWS_REGION_US_EAST_1}:{aws_provider.identity.account}:snapshot/{snapshot.id}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user