From c159a28016d992f1f703a68c1f83f69215bed450 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Thu, 30 Jan 2025 17:16:18 +0545 Subject: [PATCH 01/24] fix(neptune): correct service name (#6743) --- .../neptune_cluster_iam_authentication_enabled.metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler/providers/aws/services/neptune/neptune_cluster_iam_authentication_enabled/neptune_cluster_iam_authentication_enabled.metadata.json b/prowler/providers/aws/services/neptune/neptune_cluster_iam_authentication_enabled/neptune_cluster_iam_authentication_enabled.metadata.json index 5be9fa6c4f..c6e29617f0 100644 --- a/prowler/providers/aws/services/neptune/neptune_cluster_iam_authentication_enabled/neptune_cluster_iam_authentication_enabled.metadata.json +++ b/prowler/providers/aws/services/neptune/neptune_cluster_iam_authentication_enabled/neptune_cluster_iam_authentication_enabled.metadata.json @@ -3,7 +3,7 @@ "CheckID": "neptune_cluster_iam_authentication_enabled", "CheckTitle": "Check if Neptune Clusters have IAM authentication enabled.", "CheckType": [], - "ServiceName": "rds", + "ServiceName": "neptune", "SubServiceName": "", "ResourceIdTemplate": "arn:aws:rds:region:account-id:db-cluster", "Severity": "medium", From 5061da6897943408cbf98a4183cfc26294411419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20Poyatos?= Date: Thu, 30 Jan 2025 13:31:43 +0100 Subject: [PATCH 02/24] feat(findings): Improve /findings/metadata performance (#6748) --- api/poetry.lock | 55 ++++++++- api/pyproject.toml | 3 +- api/src/backend/api/db_router.py | 8 +- api/src/backend/api/filters.py | 47 ++++---- api/src/backend/api/specs/v1.yaml | 141 +----------------------- api/src/backend/api/tests/test_views.py | 32 +++--- api/src/backend/api/v1/serializers.py | 3 +- api/src/backend/api/v1/urls.py | 22 ++-- api/src/backend/api/v1/views.py | 85 +++++++------- api/src/backend/config/django/devel.py | 6 + api/src/backend/tasks/jobs/scan.py | 8 +- 11 files changed, 178 insertions(+), 232 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index ec24e9da8d..491b06933a 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -252,6 +252,20 @@ files = [ [package.dependencies] cryptography = "*" +[[package]] +name = "autopep8" +version = "2.3.2" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.9" +files = [ + {file = "autopep8-2.3.2-py2.py3-none-any.whl", hash = "sha256:ce8ad498672c845a0c3de2629c15b635ec2b05ef8177a6e7c91c74f3e9b51128"}, + {file = "autopep8-2.3.2.tar.gz", hash = "sha256:89440a4f969197b69a995e4ce0661b031f455a9f776d2c5ba3dbd83466931758"}, +] + +[package.dependencies] +pycodestyle = ">=2.12.0" + [[package]] name = "awsipranges" version = "0.3.3" @@ -1488,6 +1502,23 @@ docs = ["Sphinx (==2.2.0)", "docutils (<0.18)", "sphinx-rtd-theme (==0.4.3)"] publish = ["build (==0.7.0)", "twine (==3.7.1)"] test = ["coveralls (==3.3.0)", "dj-database-url (==0.5.0)", "freezegun (==1.1.0)", "psycopg2 (>=2.8.4,<3.0.0)", "pytest (==6.2.5)", "pytest-benchmark (==3.4.1)", "pytest-cov (==3.0.0)", "pytest-django (==4.4.0)", "pytest-freezegun (==0.4.2)", "pytest-lazy-fixture (==0.6.3)", "snapshottest (==0.6.0)", "tox (==3.24.4)"] +[[package]] +name = "django-silk" +version = "5.3.2" +description = "Silky smooth profiling for the Django Framework" +optional = false +python-versions = ">=3.9" +files = [ + {file = "django_silk-5.3.2-py3-none-any.whl", hash = "sha256:49f1caebfda28b1707f0cfef524e0476beb82b8c5e40f5ccff7f73a6b4f6d3ac"}, + {file = "django_silk-5.3.2.tar.gz", hash = "sha256:b0db54eebedb8d16f572321bd6daccac0bd3f547ae2618bb45d96fe8fc02229d"}, +] + +[package.dependencies] +autopep8 = "*" +Django = ">=4.2" +gprof2dot = ">=2017.09.19" +sqlparse = "*" + [[package]] name = "django-timezone-field" version = "7.1" @@ -1984,6 +2015,17 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] +[[package]] +name = "gprof2dot" +version = "2024.6.6" +description = "Generate a dot graph from the output of several profilers." +optional = false +python-versions = ">=3.8" +files = [ + {file = "gprof2dot-2024.6.6-py2.py3-none-any.whl", hash = "sha256:45b14ad7ce64e299c8f526881007b9eb2c6b75505d5613e96e66ee4d5ab33696"}, + {file = "gprof2dot-2024.6.6.tar.gz", hash = "sha256:fa1420c60025a9eb7734f65225b4da02a10fc6dd741b37fa129bc6b41951e5ab"}, +] + [[package]] name = "grapheme" version = "0.6.0" @@ -3603,6 +3645,17 @@ files = [ [package.dependencies] pyasn1 = ">=0.4.6,<0.7.0" +[[package]] +name = "pycodestyle" +version = "2.12.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, +] + [[package]] name = "pycparser" version = "2.22" @@ -5070,4 +5123,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.13" -content-hash = "af973deaabe32ea4179c44ae702f6a8cc359aabe6f1641dcf2b05534b0bbb3d5" +content-hash = "6465edb36efd1fa6db06d4103fea8046951acc3f4f8b357facaaa34ae2bc74bd" diff --git a/api/pyproject.toml b/api/pyproject.toml index 74d213b0fd..3c7338eb53 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -8,7 +8,7 @@ description = "Prowler's API (Django/DRF)" license = "Apache-2.0" name = "prowler-api" package-mode = false -version = "1.3.1" +version = "1.3.2" [tool.poetry.dependencies] celery = {extras = ["pytest"], version = "^5.4.0"} @@ -37,6 +37,7 @@ uuid6 = "2024.7.10" [tool.poetry.group.dev.dependencies] bandit = "1.7.9" coverage = "7.5.4" +django-silk = "5.3.2" docker = "7.1.0" freezegun = "1.5.1" mypy = "1.10.1" diff --git a/api/src/backend/api/db_router.py b/api/src/backend/api/db_router.py index f3a986ac20..939672f88e 100644 --- a/api/src/backend/api/db_router.py +++ b/api/src/backend/api/db_router.py @@ -4,13 +4,17 @@ class MainRouter: def db_for_read(self, model, **hints): # noqa: F841 model_table_name = model._meta.db_table - if model_table_name.startswith("django_"): + if model_table_name.startswith("django_") or model_table_name.startswith( + "silk_" + ): return self.admin_db return None def db_for_write(self, model, **hints): # noqa: F841 model_table_name = model._meta.db_table - if model_table_name.startswith("django_"): + if model_table_name.startswith("django_") or model_table_name.startswith( + "silk_" + ): return self.admin_db return None diff --git a/api/src/backend/api/filters.py b/api/src/backend/api/filters.py index 1042c3aa86..0ace11fe9c 100644 --- a/api/src/backend/api/filters.py +++ b/api/src/backend/api/filters.py @@ -319,26 +319,27 @@ class FindingFilter(FilterSet): field_name="resources__type", lookup_expr="icontains" ) - resource_tag_key = CharFilter(field_name="resources__tags__key") - resource_tag_key__in = CharInFilter( - field_name="resources__tags__key", lookup_expr="in" - ) - resource_tag_key__icontains = CharFilter( - field_name="resources__tags__key", lookup_expr="icontains" - ) - resource_tag_value = CharFilter(field_name="resources__tags__value") - resource_tag_value__in = CharInFilter( - field_name="resources__tags__value", lookup_expr="in" - ) - resource_tag_value__icontains = CharFilter( - field_name="resources__tags__value", lookup_expr="icontains" - ) - resource_tags = CharInFilter( - method="filter_resource_tag", - lookup_expr="in", - help_text="Filter by resource tags `key:value` pairs.\nMultiple values may be " - "separated by commas.", - ) + # Temporarily disabled until we implement tag filtering in the UI + # resource_tag_key = CharFilter(field_name="resources__tags__key") + # resource_tag_key__in = CharInFilter( + # field_name="resources__tags__key", lookup_expr="in" + # ) + # resource_tag_key__icontains = CharFilter( + # field_name="resources__tags__key", lookup_expr="icontains" + # ) + # resource_tag_value = CharFilter(field_name="resources__tags__value") + # resource_tag_value__in = CharInFilter( + # field_name="resources__tags__value", lookup_expr="in" + # ) + # resource_tag_value__icontains = CharFilter( + # field_name="resources__tags__value", lookup_expr="icontains" + # ) + # resource_tags = CharInFilter( + # method="filter_resource_tag", + # lookup_expr="in", + # help_text="Filter by resource tags `key:value` pairs.\nMultiple values may be " + # "separated by commas.", + # ) scan = UUIDFilter(method="filter_scan_id") scan__in = UUIDInFilter(method="filter_scan_id_in") @@ -374,12 +375,6 @@ class FindingFilter(FilterSet): }, } - @property - def qs(self): - # Force distinct results to prevent duplicates with many-to-many relationships - parent_qs = super().qs - return parent_qs.distinct() - # Convert filter values to UUIDv7 values for use with partitioning def filter_scan_id(self, queryset, name, value): try: diff --git a/api/src/backend/api/specs/v1.yaml b/api/src/backend/api/specs/v1.yaml index cb355b1aa7..7448843b54 100644 --- a/api/src/backend/api/specs/v1.yaml +++ b/api/src/backend/api/specs/v1.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: Prowler API - version: 1.3.1 + version: 1.3.2 description: |- Prowler API specification. @@ -478,51 +478,6 @@ paths: description: Multiple values may be separated by commas. explode: false style: form - - in: query - name: filter[resource_tag_key] - schema: - type: string - - in: query - name: filter[resource_tag_key__icontains] - schema: - type: string - - in: query - name: filter[resource_tag_key__in] - schema: - type: array - items: - type: string - description: Multiple values may be separated by commas. - explode: false - style: form - - in: query - name: filter[resource_tag_value] - schema: - type: string - - in: query - name: filter[resource_tag_value__icontains] - schema: - type: string - - in: query - name: filter[resource_tag_value__in] - schema: - type: array - items: - type: string - description: Multiple values may be separated by commas. - explode: false - style: form - - in: query - name: filter[resource_tags] - schema: - type: array - items: - type: string - description: |- - Filter by resource tags `key:value` pairs. - Multiple values may be separated by commas. - explode: false - style: form - in: query name: filter[resource_type] schema: @@ -1028,51 +983,6 @@ paths: description: Multiple values may be separated by commas. explode: false style: form - - in: query - name: filter[resource_tag_key] - schema: - type: string - - in: query - name: filter[resource_tag_key__icontains] - schema: - type: string - - in: query - name: filter[resource_tag_key__in] - schema: - type: array - items: - type: string - description: Multiple values may be separated by commas. - explode: false - style: form - - in: query - name: filter[resource_tag_value] - schema: - type: string - - in: query - name: filter[resource_tag_value__icontains] - schema: - type: string - - in: query - name: filter[resource_tag_value__in] - schema: - type: array - items: - type: string - description: Multiple values may be separated by commas. - explode: false - style: form - - in: query - name: filter[resource_tags] - schema: - type: array - items: - type: string - description: |- - Filter by resource tags `key:value` pairs. - Multiple values may be separated by commas. - explode: false - style: form - in: query name: filter[resource_type] schema: @@ -1280,7 +1190,6 @@ paths: - services - regions - resource_types - - tags description: endpoint return only specific fields in the response on a per-type basis by including a fields[TYPE] query parameter. explode: false @@ -1498,51 +1407,6 @@ paths: description: Multiple values may be separated by commas. explode: false style: form - - in: query - name: filter[resource_tag_key] - schema: - type: string - - in: query - name: filter[resource_tag_key__icontains] - schema: - type: string - - in: query - name: filter[resource_tag_key__in] - schema: - type: array - items: - type: string - description: Multiple values may be separated by commas. - explode: false - style: form - - in: query - name: filter[resource_tag_value] - schema: - type: string - - in: query - name: filter[resource_tag_value__icontains] - schema: - type: string - - in: query - name: filter[resource_tag_value__in] - schema: - type: array - items: - type: string - description: Multiple values may be separated by commas. - explode: false - style: form - - in: query - name: filter[resource_tags] - schema: - type: array - items: - type: string - description: |- - Filter by resource tags `key:value` pairs. - Multiple values may be separated by commas. - explode: false - style: form - in: query name: filter[resource_type] schema: @@ -6081,13 +5945,10 @@ components: type: array items: type: string - tags: - description: Tags are described as key-value pairs. required: - services - regions - resource_types - - tags FindingMetadataResponse: type: object properties: diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index 184843af18..0173797062 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -2454,15 +2454,16 @@ class TestFindingViewSet: ("search", "ec2", 2), # full text search on finding tags ("search", "value2", 2), - ("resource_tag_key", "key", 2), - ("resource_tag_key__in", "key,key2", 2), - ("resource_tag_key__icontains", "key", 2), - ("resource_tag_value", "value", 2), - ("resource_tag_value__in", "value,value2", 2), - ("resource_tag_value__icontains", "value", 2), - ("resource_tags", "key:value", 2), - ("resource_tags", "not:exists", 0), - ("resource_tags", "not:exists,key:value", 2), + # Temporary disabled until we implement tag filtering in the UI + # ("resource_tag_key", "key", 2), + # ("resource_tag_key__in", "key,key2", 2), + # ("resource_tag_key__icontains", "key", 2), + # ("resource_tag_value", "value", 2), + # ("resource_tag_value__in", "value,value2", 2), + # ("resource_tag_value__icontains", "value", 2), + # ("resource_tags", "key:value", 2), + # ("resource_tags", "not:exists", 0), + # ("resource_tags", "not:exists,key:value", 2), ] ), ) @@ -2611,7 +2612,8 @@ class TestFindingViewSet: expected_services = {"ec2", "s3"} expected_regions = {"eu-west-1", "us-east-1"} - expected_tags = {"key": ["value"], "key2": ["value2"]} + # Temporarily disabled until we implement tag filtering in the UI + # expected_tags = {"key": ["value"], "key2": ["value2"]} expected_resource_types = {"prowler-test"} assert data["data"]["type"] == "findings-metadata" @@ -2621,7 +2623,7 @@ class TestFindingViewSet: assert ( set(data["data"]["attributes"]["resource_types"]) == expected_resource_types ) - assert data["data"]["attributes"]["tags"] == expected_tags + # assert data["data"]["attributes"]["tags"] == expected_tags def test_findings_metadata_severity_retrieve( self, authenticated_client, findings_fixture @@ -2638,7 +2640,8 @@ class TestFindingViewSet: expected_services = {"s3"} expected_regions = {"eu-west-1"} - expected_tags = {"key": ["value"], "key2": ["value2"]} + # Temporary disabled until we implement tag filtering in the UI + # expected_tags = {"key": ["value"], "key2": ["value2"]} expected_resource_types = {"prowler-test"} assert data["data"]["type"] == "findings-metadata" @@ -2648,7 +2651,7 @@ class TestFindingViewSet: assert ( set(data["data"]["attributes"]["resource_types"]) == expected_resource_types ) - assert data["data"]["attributes"]["tags"] == expected_tags + # assert data["data"]["attributes"]["tags"] == expected_tags def test_findings_metadata_future_date(self, authenticated_client): response = authenticated_client.get( @@ -2660,7 +2663,8 @@ class TestFindingViewSet: assert data["data"]["id"] is None assert data["data"]["attributes"]["services"] == [] assert data["data"]["attributes"]["regions"] == [] - assert data["data"]["attributes"]["tags"] == {} + # Temporary disabled until we implement tag filtering in the UI + # assert data["data"]["attributes"]["tags"] == {} assert data["data"]["attributes"]["resource_types"] == [] def test_findings_metadata_invalid_date(self, authenticated_client): diff --git a/api/src/backend/api/v1/serializers.py b/api/src/backend/api/v1/serializers.py index 6046799d0c..4f17d2d61c 100644 --- a/api/src/backend/api/v1/serializers.py +++ b/api/src/backend/api/v1/serializers.py @@ -933,7 +933,8 @@ class FindingMetadataSerializer(serializers.Serializer): resource_types = serializers.ListField( child=serializers.CharField(), allow_empty=True ) - tags = serializers.JSONField(help_text="Tags are described as key-value pairs.") + # Temporarily disabled until we implement tag filtering in the UI + # tags = serializers.JSONField(help_text="Tags are described as key-value pairs.") class Meta: resource_name = "findings-metadata" diff --git a/api/src/backend/api/v1/urls.py b/api/src/backend/api/v1/urls.py index fd3f9ad18a..6b230960e1 100644 --- a/api/src/backend/api/v1/urls.py +++ b/api/src/backend/api/v1/urls.py @@ -1,30 +1,31 @@ +from django.conf import settings from django.urls import include, path from drf_spectacular.views import SpectacularRedocView from rest_framework_nested import routers from api.v1.views import ( + ComplianceOverviewViewSet, CustomTokenObtainView, CustomTokenRefreshView, FindingViewSet, - MembershipViewSet, - ProviderGroupViewSet, - ProviderGroupProvidersRelationshipView, - ProviderSecretViewSet, - InvitationViewSet, InvitationAcceptViewSet, - RoleViewSet, - RoleProviderGroupRelationshipView, - UserRoleRelationshipView, + InvitationViewSet, + MembershipViewSet, OverviewViewSet, - ComplianceOverviewViewSet, + ProviderGroupProvidersRelationshipView, + ProviderGroupViewSet, + ProviderSecretViewSet, ProviderViewSet, ResourceViewSet, + RoleProviderGroupRelationshipView, + RoleViewSet, ScanViewSet, ScheduleViewSet, SchemaView, TaskViewSet, TenantMembersViewSet, TenantViewSet, + UserRoleRelationshipView, UserViewSet, ) @@ -112,3 +113,6 @@ urlpatterns = [ path("schema", SchemaView.as_view(), name="schema"), path("docs", SpectacularRedocView.as_view(url_name="schema"), name="docs"), ] + +if settings.DEBUG: + urlpatterns += [path("silk/", include("silk.urls", namespace="silk"))] diff --git a/api/src/backend/api/v1/views.py b/api/src/backend/api/v1/views.py index debfcdd57c..3ecb6474fb 100644 --- a/api/src/backend/api/v1/views.py +++ b/api/src/backend/api/v1/views.py @@ -4,7 +4,7 @@ from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.search import SearchQuery from django.db import transaction from django.db.models import Count, F, OuterRef, Prefetch, Q, Subquery, Sum -from django.db.models.functions import Coalesce, JSONObject +from django.db.models.functions import Coalesce from django.urls import reverse from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_control @@ -193,7 +193,7 @@ class SchemaView(SpectacularAPIView): def get(self, request, *args, **kwargs): spectacular_settings.TITLE = "Prowler API" - spectacular_settings.VERSION = "1.3.1" + spectacular_settings.VERSION = "1.3.2" spectacular_settings.DESCRIPTION = ( "Prowler API specification.\n\nThis file is auto-generated." ) @@ -1392,48 +1392,59 @@ class FindingViewSet(BaseRLSViewSet): @action(detail=False, methods=["get"], url_name="metadata") def metadata(self, request): + tenant_id = self.request.tenant_id queryset = self.get_queryset() filtered_queryset = self.filter_queryset(queryset) - result = filtered_queryset.aggregate( - services=ArrayAgg("resources__service", flat=True, distinct=True), - regions=ArrayAgg("resources__region", flat=True, distinct=True), - tags=ArrayAgg( - JSONObject( - key=F("resources__tags__key"), value=F("resources__tags__value") - ), - distinct=True, - filter=Q(resources__tags__key__isnull=False), - ), - resource_types=ArrayAgg("resources__type", flat=True, distinct=True), - ) - if result["services"] is None: - result["services"] = [] - if result["regions"] is None: - result["regions"] = [] - if result["regions"] is None: - result["regions"] = [] - if result["resource_types"] is None: - result["resource_types"] = [] - if result["tags"] is None: - result["tags"] = [] + relevant_resources = Resource.objects.filter( + tenant_id=tenant_id, findings__in=filtered_queryset + ).distinct() - tags_dict = {} - for t in result["tags"]: - key, value = t["key"], t["value"] - if key not in tags_dict: - tags_dict[key] = [] - tags_dict[key].append(value) - - result["tags"] = tags_dict - - serializer = self.get_serializer( - data=result, + services = ( + relevant_resources.values_list("service", flat=True) + .distinct() + .order_by("service") ) + regions = ( + relevant_resources.exclude(region="") + .values_list("region", flat=True) + .distinct() + .order_by("region") + ) + + resource_types = ( + relevant_resources.values_list("type", flat=True) + .distinct() + .order_by("type") + ) + + # Temporarily disabled until we implement tag filtering in the UI + # tag_data = ( + # relevant_resources + # .filter(tags__key__isnull=False, tags__value__isnull=False) + # .exclude(tags__key="") + # .exclude(tags__value="") + # .values("tags__key", "tags__value") + # .distinct() + # .order_by("tags__key", "tags__value") + # ) + # + # tags_dict = {} + # for row in tag_data: + # k, v = row["tags__key"], row["tags__value"] + # tags_dict.setdefault(k, []).append(v) + + result = { + "services": list(services), + "regions": list(regions), + "resource_types": list(resource_types), + # "tags": tags_dict + } + + serializer = self.get_serializer(data=result) serializer.is_valid(raise_exception=True) - - return Response(data=serializer.data, status=status.HTTP_200_OK) + return Response(serializer.data, status=status.HTTP_200_OK) @extend_schema_view( diff --git a/api/src/backend/config/django/devel.py b/api/src/backend/config/django/devel.py index 825e1ce36a..6ee92a6ecb 100644 --- a/api/src/backend/config/django/devel.py +++ b/api/src/backend/config/django/devel.py @@ -37,3 +37,9 @@ REST_FRAMEWORK["DEFAULT_FILTER_BACKENDS"] = tuple( # noqa: F405 ) + ("api.filters.CustomDjangoFilterBackend",) SECRETS_ENCRYPTION_KEY = "ZMiYVo7m4Fbe2eXXPyrwxdJss2WSalXSv3xHBcJkPl0=" + +MIDDLEWARE += [ # noqa: F405 + "silk.middleware.SilkyMiddleware", +] + +INSTALLED_APPS += ["silk"] # noqa: F405 diff --git a/api/src/backend/tasks/jobs/scan.py b/api/src/backend/tasks/jobs/scan.py index 25993f5bbc..89612ae6fd 100644 --- a/api/src/backend/tasks/jobs/scan.py +++ b/api/src/backend/tasks/jobs/scan.py @@ -152,6 +152,9 @@ def perform_prowler_scan( for progress, findings in prowler_scan.scan(): for finding in findings: + if finding is None: + logger.error(f"None finding detected on scan {scan_id}.") + continue for attempt in range(CELERY_DEADLOCK_ATTEMPTS): try: with rls_transaction(tenant_id): @@ -176,7 +179,10 @@ def perform_prowler_scan( # Update resource fields if necessary updated_fields = [] - if resource_instance.region != finding.region: + if ( + finding.region + and resource_instance.region != finding.region + ): resource_instance.region = finding.region updated_fields.append("region") if resource_instance.service != finding.service_name: From 5bfaedf903bf06e0b3b07a01c8b3d818a84bdb15 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Thu, 30 Jan 2025 14:05:39 +0100 Subject: [PATCH 03/24] fix: Enable hot reloading when using Docker Compose for UI (#6750) --- docker-compose-dev.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index d49af9dfbb..c8ecea96d5 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -35,6 +35,9 @@ services: required: false ports: - 3000:3000 + volumes: + - "./ui:/app" + - "/app/node_modules" postgres: image: postgres:16.3-alpine3.20 From 5186e029b3fc89db58517c6f059739ee256dddb0 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Thu, 30 Jan 2025 20:48:51 +0545 Subject: [PATCH 04/24] fix(set_report_color): Add more details to error (#6751) --- prowler/lib/outputs/outputs.py | 4 +++- tests/lib/outputs/outputs_test.py | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/prowler/lib/outputs/outputs.py b/prowler/lib/outputs/outputs.py index 2a61e17c36..b4699157d7 100644 --- a/prowler/lib/outputs/outputs.py +++ b/prowler/lib/outputs/outputs.py @@ -83,7 +83,9 @@ def set_report_color(status: str, muted: bool = False) -> str: elif status == "MANUAL": color = Fore.YELLOW else: - raise Exception("Invalid Report Status. Must be PASS, FAIL or MANUAL.") + raise Exception( + f"Invalid Report Status: {status}. Must be PASS, FAIL or MANUAL." + ) return color diff --git a/tests/lib/outputs/outputs_test.py b/tests/lib/outputs/outputs_test.py index 624366af5e..dbf7d5c8bd 100644 --- a/tests/lib/outputs/outputs_test.py +++ b/tests/lib/outputs/outputs_test.py @@ -21,7 +21,6 @@ from prowler.lib.outputs.utils import ( class TestOutputs: - def test_set_report_color(self): test_status = ["PASS", "FAIL", "MANUAL"] test_colors = [Fore.GREEN, Fore.RED, Fore.YELLOW] @@ -35,8 +34,9 @@ class TestOutputs: with pytest.raises(Exception) as exc: set_report_color(test_status) - assert "Invalid Report Status. Must be PASS, FAIL or MANUAL" in str(exc.value) - assert exc.type == Exception + assert "Invalid Report Status: INVALID. Must be PASS, FAIL or MANUAL" in str( + exc.value + ) def test_unroll_list_no_separator(self): list = ["test", "test1", "test2"] From 712ba84f068e7f158658e1129363cd46ab749f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20Poyatos?= Date: Thu, 30 Jan 2025 16:06:12 +0100 Subject: [PATCH 05/24] feat(scans): Optimize read queries during scans (#6753) --- api/src/backend/tasks/jobs/scan.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/api/src/backend/tasks/jobs/scan.py b/api/src/backend/tasks/jobs/scan.py index 89612ae6fd..b11366a859 100644 --- a/api/src/backend/tasks/jobs/scan.py +++ b/api/src/backend/tasks/jobs/scan.py @@ -228,8 +228,10 @@ def perform_prowler_scan( last_first_seen_at = None if finding_uid not in last_status_cache: most_recent_finding = ( - Finding.objects.filter(uid=finding_uid) - .order_by("-id") + Finding.all_objects.filter( + tenant_id=tenant_id, uid=finding_uid + ) + .order_by("-inserted_at") .values("status", "first_seen_at") .first() ) @@ -378,7 +380,7 @@ def aggregate_findings(tenant_id: str, scan_id: str): - muted_changed: Muted findings with a delta of 'changed'. """ with rls_transaction(tenant_id): - findings = Finding.objects.filter(scan_id=scan_id) + findings = Finding.objects.filter(tenant_id=tenant_id, scan_id=scan_id) aggregation = findings.values( "check_id", From 627c11503f1484db1728184bf19bf0dd458d4cdd Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Thu, 30 Jan 2025 21:46:43 +0545 Subject: [PATCH 06/24] fix(db_event): Handle other events (#6754) --- ...ds_instance_critical_event_subscription.py | 3 ++ ...stance_critical_event_subscription_test.py | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/prowler/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription.py b/prowler/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription.py index e2f379232b..43751d7369 100644 --- a/prowler/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription.py +++ b/prowler/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription.py @@ -54,6 +54,9 @@ class rds_instance_critical_event_subscription(Check): }: report.status = "FAIL" report.status_extended = "RDS instance event category of maintenance is not subscribed." + else: + report.status = "FAIL" + report.status_extended = "RDS instance event categories of maintenance, configuration change, and failure are not subscribed." findings.append(report) return findings diff --git a/tests/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription_test.py b/tests/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription_test.py index d72df2303a..02729be231 100644 --- a/tests/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription_test.py +++ b/tests/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription_test.py @@ -483,3 +483,52 @@ class Test_rds_instance_critical_event_subscription: == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) assert result[0].resource_tags == [] + + @mock_aws + def test_rds_instance_event_invalid(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_parameter_group( + DBParameterGroupName="test", + DBParameterGroupFamily="default.aurora-postgresql14", + Description="test parameter group", + ) + conn.create_event_subscription( + SubscriptionName="TestSub", + SnsTopicArn=f"arn:aws:sns:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:test", + SourceType="db-instance", + EventCategories=["invalid"], + Enabled=True, + ) + from prowler.providers.aws.services.rds.rds_service import RDS + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_instance_critical_event_subscription.rds_instance_critical_event_subscription.rds_client", + new=RDS(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.rds.rds_instance_critical_event_subscription.rds_instance_critical_event_subscription import ( + rds_instance_critical_event_subscription, + ) + + check = rds_instance_critical_event_subscription() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "RDS instance event categories of maintenance, configuration change, and failure are not subscribed." + ) + assert result[0].resource_id == "TestSub" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" + ) + assert result[0].resource_tags == [] From 18b7b48a991ca0941bd8c41f4f8963ae2e49db3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:07:17 +0100 Subject: [PATCH 07/24] chore(deps): bump microsoft-kiota-abstractions from 1.6.8 to 1.9.1 (#6734) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 188 ++++++++++--------------------------------------- pyproject.toml | 2 +- 2 files changed, 37 insertions(+), 153 deletions(-) diff --git a/poetry.lock b/poetry.lock index b0e6e0e4ad..cbb2504f70 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "about-time" @@ -2359,13 +2359,13 @@ files = [ [[package]] name = "microsoft-kiota-abstractions" -version = "1.6.8" +version = "1.9.1" description = "Core abstractions for kiota generated libraries in Python" optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" files = [ - {file = "microsoft_kiota_abstractions-1.6.8-py3-none-any.whl", hash = "sha256:12819dee24d5aaa31e99683d938f65e50cbc446de087df244cd26c3326ec4e15"}, - {file = "microsoft_kiota_abstractions-1.6.8.tar.gz", hash = "sha256:7070affabfa7182841646a0c8491cbb240af366aff2b9132f0caa45c4837dd78"}, + {file = "microsoft_kiota_abstractions-1.9.1-py3-none-any.whl", hash = "sha256:aca6147946fc56167038faddda0a07026b0ec4e76666520586de7ac5c1233295"}, + {file = "microsoft_kiota_abstractions-1.9.1.tar.gz", hash = "sha256:291f4f88bec5452b2c6bafed4927640d2c7d2f41b08c941ee786f6ecad3dcaa5"}, ] [package.dependencies] @@ -2375,19 +2375,19 @@ std-uritemplate = ">=2.0.0" [[package]] name = "microsoft-kiota-authentication-azure" -version = "1.6.8" +version = "1.9.1" description = "Core abstractions for kiota generated libraries in Python" optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" files = [ - {file = "microsoft_kiota_authentication_azure-1.6.8-py3-none-any.whl", hash = "sha256:50455789b7133e27fbccec839d93e40d2637d18593a93921ae1338880c5b5b3b"}, - {file = "microsoft_kiota_authentication_azure-1.6.8.tar.gz", hash = "sha256:fef23f43cd4d3b9ef839c8b3d1f675ec4a1120c150f963d8c4551c5e19ac3b36"}, + {file = "microsoft_kiota_authentication_azure-1.9.1-py3-none-any.whl", hash = "sha256:3a3030b01e0cbf007736ec6548ac483742e04ad0d20979b49e293a683f839fc6"}, + {file = "microsoft_kiota_authentication_azure-1.9.1.tar.gz", hash = "sha256:8ba31b1ecf78777128daf3191c7ecd28bfc2d10d0fe80260dd5c07a9ccd5d7f5"}, ] [package.dependencies] aiohttp = ">=3.8.0" azure-core = ">=1.21.1" -microsoft-kiota-abstractions = ">=1.6.8,<1.7.0" +microsoft-kiota-abstractions = ">=1.9.1,<1.10.0" opentelemetry-api = ">=1.27.0" opentelemetry-sdk = ">=1.27.0" @@ -2408,83 +2408,61 @@ microsoft-kiota_abstractions = ">=1.0.0,<2.0.0" opentelemetry-api = ">=1.20.0" opentelemetry-sdk = ">=1.20.0" -[[package]] -name = "microsoft-kiota-http" -version = "1.6.8" -description = "Core abstractions for kiota generated libraries in Python" -optional = false -python-versions = "<4.0,>=3.8" -files = [ - {file = "microsoft_kiota_http-1.6.8-py3-none-any.whl", hash = "sha256:7ff76a308351d885453185d6a6538c47a64ebdc7661cce46a904e89e2ceb9a1d"}, - {file = "microsoft_kiota_http-1.6.8.tar.gz", hash = "sha256:67242690b79a30c0cadf823675249269e4bc020283e3d65b33af7d771df64df8"}, -] - -[package.dependencies] -httpx = {version = ">=0.28", extras = ["http2"]} -microsoft-kiota-abstractions = ">=1.6.8,<1.7.0" -opentelemetry-api = ">=1.27.0" -opentelemetry-sdk = ">=1.27.0" -urllib3 = ">=2.2.2,<3.0.0" - [[package]] name = "microsoft-kiota-serialization-form" -version = "1.6.8" +version = "1.9.1" description = "Core abstractions for kiota generated libraries in Python" optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" files = [ - {file = "microsoft_kiota_serialization_form-1.6.8-py3-none-any.whl", hash = "sha256:ca7dd19e173aa87c68b38c5056cc0921570c8c86f3ba5511d1616cf97e2f0f67"}, - {file = "microsoft_kiota_serialization_form-1.6.8.tar.gz", hash = "sha256:bb9eb98b3abf596b4bfe208014dff948361ff48a757316ac58e19c31ab8d640a"}, + {file = "microsoft_kiota_serialization_form-1.9.1-py3-none-any.whl", hash = "sha256:d86c0dc08b51288f2851a265dde729b200998bca1dd1681bbebcb27cd274ba34"}, + {file = "microsoft_kiota_serialization_form-1.9.1.tar.gz", hash = "sha256:58b81eca5e0ad66bcbd6a4b65ba91e6255d3510f4c4f576fb4f5e83ca9a310c3"}, ] [package.dependencies] -microsoft-kiota-abstractions = ">=1.6.8,<1.7.0" -pendulum = ">=3.0.0b1" +microsoft-kiota-abstractions = ">=1.9.1,<1.10.0" [[package]] name = "microsoft-kiota-serialization-json" -version = "1.6.8" +version = "1.9.1" description = "Core abstractions for kiota generated libraries in Python" optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" files = [ - {file = "microsoft_kiota_serialization_json-1.6.8-py3-none-any.whl", hash = "sha256:2734c2ad64cc089441279e4962f6fedf41af040730f6eab1533890cd5377aff5"}, - {file = "microsoft_kiota_serialization_json-1.6.8.tar.gz", hash = "sha256:89e2dd0eb4eaaa6ab74fa89ab5d84c5a53464e73b85eb7085f0aa4560a2b8183"}, + {file = "microsoft_kiota_serialization_json-1.9.1-py3-none-any.whl", hash = "sha256:ab752bf642a77266713bac3942a4c547dde60b917f674a9ab63261490fecf841"}, + {file = "microsoft_kiota_serialization_json-1.9.1.tar.gz", hash = "sha256:0039458b885875daf246f1e8c0296551695e0ec70f3e4689b00b270ce923c0cc"}, ] [package.dependencies] -microsoft-kiota-abstractions = ">=1.6.8,<1.7.0" -pendulum = ">=3.0.0b1" +microsoft-kiota-abstractions = ">=1.9.1,<1.10.0" [[package]] name = "microsoft-kiota-serialization-multipart" -version = "1.6.8" +version = "1.9.1" description = "Core abstractions for kiota generated libraries in Python" optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" files = [ - {file = "microsoft_kiota_serialization_multipart-1.6.8-py3-none-any.whl", hash = "sha256:1ecdd15dd1f78aed031d7d1828b6fbc00c633542d863c23f96fdd0a61bfb189a"}, - {file = "microsoft_kiota_serialization_multipart-1.6.8.tar.gz", hash = "sha256:3d95c6d7186588af7a1d3aa852ce42077f80487b8b3c60e36fe109a8b4918c03"}, + {file = "microsoft_kiota_serialization_multipart-1.9.1-py3-none-any.whl", hash = "sha256:1ee69d8e3b2c0d24431b85fc1628534f97dcafed45dcb6bb3f0143824ce8a665"}, + {file = "microsoft_kiota_serialization_multipart-1.9.1.tar.gz", hash = "sha256:288790a486aad33aac0a7329f05b8851e9be82f50cc9a8f19fe6f267a4f2b183"}, ] [package.dependencies] -microsoft-kiota-abstractions = ">=1.6.8,<1.7.0" -pendulum = ">=3.0.0b1" +microsoft-kiota-abstractions = ">=1.9.1,<1.10.0" [[package]] name = "microsoft-kiota-serialization-text" -version = "1.6.8" +version = "1.9.1" description = "Core abstractions for kiota generated libraries in Python" optional = false -python-versions = "<4.0,>=3.8" +python-versions = "<4.0,>=3.9" files = [ - {file = "microsoft_kiota_serialization_text-1.6.8-py3-none-any.whl", hash = "sha256:4e5e287a614d362f864b5061dca0861c3f70b8792ec72967d1bff23944da1e80"}, - {file = "microsoft_kiota_serialization_text-1.6.8.tar.gz", hash = "sha256:687d4858337eaf4f351b12ed1c6c934d869560f54ee3855bfdde589660e07208"}, + {file = "microsoft_kiota_serialization_text-1.9.1-py3-none-any.whl", hash = "sha256:021fbfad887f7525f9e1c8bbe225d0aa1b25befe54357ae0f739f47576d946ff"}, + {file = "microsoft_kiota_serialization_text-1.9.1.tar.gz", hash = "sha256:b4b1f61c2388edd704ab33a1792f92f3507db9648d389197a625e152b52743ab"}, ] [package.dependencies] -microsoft-kiota-abstractions = ">=1.6.8,<1.7.0" -python-dateutil = "2.9.0.post0" +microsoft-kiota-abstractions = ">=1.9.1,<1.10.0" [[package]] name = "mkdocs" @@ -3241,105 +3219,6 @@ files = [ {file = "pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24"}, ] -[[package]] -name = "pendulum" -version = "3.0.0" -description = "Python datetimes made easy" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pendulum-3.0.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2cf9e53ef11668e07f73190c805dbdf07a1939c3298b78d5a9203a86775d1bfd"}, - {file = "pendulum-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fb551b9b5e6059377889d2d878d940fd0bbb80ae4810543db18e6f77b02c5ef6"}, - {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c58227ac260d5b01fc1025176d7b31858c9f62595737f350d22124a9a3ad82d"}, - {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60fb6f415fea93a11c52578eaa10594568a6716602be8430b167eb0d730f3332"}, - {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b69f6b4dbcb86f2c2fe696ba991e67347bcf87fe601362a1aba6431454b46bde"}, - {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:138afa9c373ee450ede206db5a5e9004fd3011b3c6bbe1e57015395cd076a09f"}, - {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:83d9031f39c6da9677164241fd0d37fbfc9dc8ade7043b5d6d62f56e81af8ad2"}, - {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c2308af4033fa534f089595bcd40a95a39988ce4059ccd3dc6acb9ef14ca44a"}, - {file = "pendulum-3.0.0-cp310-none-win_amd64.whl", hash = "sha256:9a59637cdb8462bdf2dbcb9d389518c0263799189d773ad5c11db6b13064fa79"}, - {file = "pendulum-3.0.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3725245c0352c95d6ca297193192020d1b0c0f83d5ee6bb09964edc2b5a2d508"}, - {file = "pendulum-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6c035f03a3e565ed132927e2c1b691de0dbf4eb53b02a5a3c5a97e1a64e17bec"}, - {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597e66e63cbd68dd6d58ac46cb7a92363d2088d37ccde2dae4332ef23e95cd00"}, - {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99a0f8172e19f3f0c0e4ace0ad1595134d5243cf75985dc2233e8f9e8de263ca"}, - {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:77d8839e20f54706aed425bec82a83b4aec74db07f26acd039905d1237a5e1d4"}, - {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afde30e8146292b059020fbc8b6f8fd4a60ae7c5e6f0afef937bbb24880bdf01"}, - {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:660434a6fcf6303c4efd36713ca9212c753140107ee169a3fc6c49c4711c2a05"}, - {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dee9e5a48c6999dc1106eb7eea3e3a50e98a50651b72c08a87ee2154e544b33e"}, - {file = "pendulum-3.0.0-cp311-none-win_amd64.whl", hash = "sha256:d4cdecde90aec2d67cebe4042fd2a87a4441cc02152ed7ed8fb3ebb110b94ec4"}, - {file = "pendulum-3.0.0-cp311-none-win_arm64.whl", hash = "sha256:773c3bc4ddda2dda9f1b9d51fe06762f9200f3293d75c4660c19b2614b991d83"}, - {file = "pendulum-3.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:409e64e41418c49f973d43a28afe5df1df4f1dd87c41c7c90f1a63f61ae0f1f7"}, - {file = "pendulum-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a38ad2121c5ec7c4c190c7334e789c3b4624798859156b138fcc4d92295835dc"}, - {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fde4d0b2024b9785f66b7f30ed59281bd60d63d9213cda0eb0910ead777f6d37"}, - {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b2c5675769fb6d4c11238132962939b960fcb365436b6d623c5864287faa319"}, - {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8af95e03e066826f0f4c65811cbee1b3123d4a45a1c3a2b4fc23c4b0dff893b5"}, - {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2165a8f33cb15e06c67070b8afc87a62b85c5a273e3aaa6bc9d15c93a4920d6f"}, - {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ad5e65b874b5e56bd942546ea7ba9dd1d6a25121db1c517700f1c9de91b28518"}, - {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17fe4b2c844bbf5f0ece69cfd959fa02957c61317b2161763950d88fed8e13b9"}, - {file = "pendulum-3.0.0-cp312-none-win_amd64.whl", hash = "sha256:78f8f4e7efe5066aca24a7a57511b9c2119f5c2b5eb81c46ff9222ce11e0a7a5"}, - {file = "pendulum-3.0.0-cp312-none-win_arm64.whl", hash = "sha256:28f49d8d1e32aae9c284a90b6bb3873eee15ec6e1d9042edd611b22a94ac462f"}, - {file = "pendulum-3.0.0-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d4e2512f4e1a4670284a153b214db9719eb5d14ac55ada5b76cbdb8c5c00399d"}, - {file = "pendulum-3.0.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:3d897eb50883cc58d9b92f6405245f84b9286cd2de6e8694cb9ea5cb15195a32"}, - {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e169cc2ca419517f397811bbe4589cf3cd13fca6dc38bb352ba15ea90739ebb"}, - {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17c3084a4524ebefd9255513692f7e7360e23c8853dc6f10c64cc184e1217ab"}, - {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:826d6e258052715f64d05ae0fc9040c0151e6a87aae7c109ba9a0ed930ce4000"}, - {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2aae97087872ef152a0c40e06100b3665d8cb86b59bc8471ca7c26132fccd0f"}, - {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ac65eeec2250d03106b5e81284ad47f0d417ca299a45e89ccc69e36130ca8bc7"}, - {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a5346d08f3f4a6e9e672187faa179c7bf9227897081d7121866358af369f44f9"}, - {file = "pendulum-3.0.0-cp37-none-win_amd64.whl", hash = "sha256:235d64e87946d8f95c796af34818c76e0f88c94d624c268693c85b723b698aa9"}, - {file = "pendulum-3.0.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:6a881d9c2a7f85bc9adafcfe671df5207f51f5715ae61f5d838b77a1356e8b7b"}, - {file = "pendulum-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d7762d2076b9b1cb718a6631ad6c16c23fc3fac76cbb8c454e81e80be98daa34"}, - {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8e36a8130819d97a479a0e7bf379b66b3b1b520e5dc46bd7eb14634338df8c"}, - {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7dc843253ac373358ffc0711960e2dd5b94ab67530a3e204d85c6e8cb2c5fa10"}, - {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a78ad3635d609ceb1e97d6aedef6a6a6f93433ddb2312888e668365908c7120"}, - {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a137e9e0d1f751e60e67d11fc67781a572db76b2296f7b4d44554761049d6"}, - {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c95984037987f4a457bb760455d9ca80467be792236b69d0084f228a8ada0162"}, - {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d29c6e578fe0f893766c0d286adbf0b3c726a4e2341eba0917ec79c50274ec16"}, - {file = "pendulum-3.0.0-cp38-none-win_amd64.whl", hash = "sha256:deaba8e16dbfcb3d7a6b5fabdd5a38b7c982809567479987b9c89572df62e027"}, - {file = "pendulum-3.0.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b11aceea5b20b4b5382962b321dbc354af0defe35daa84e9ff3aae3c230df694"}, - {file = "pendulum-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a90d4d504e82ad236afac9adca4d6a19e4865f717034fc69bafb112c320dcc8f"}, - {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:825799c6b66e3734227756fa746cc34b3549c48693325b8b9f823cb7d21b19ac"}, - {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad769e98dc07972e24afe0cff8d365cb6f0ebc7e65620aa1976fcfbcadc4c6f3"}, - {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6fc26907eb5fb8cc6188cc620bc2075a6c534d981a2f045daa5f79dfe50d512"}, - {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c717eab1b6d898c00a3e0fa7781d615b5c5136bbd40abe82be100bb06df7a56"}, - {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3ddd1d66d1a714ce43acfe337190be055cdc221d911fc886d5a3aae28e14b76d"}, - {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:822172853d7a9cf6da95d7b66a16c7160cb99ae6df55d44373888181d7a06edc"}, - {file = "pendulum-3.0.0-cp39-none-win_amd64.whl", hash = "sha256:840de1b49cf1ec54c225a2a6f4f0784d50bd47f68e41dc005b7f67c7d5b5f3ae"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b1f74d1e6ffe5d01d6023870e2ce5c2191486928823196f8575dcc786e107b1"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:729e9f93756a2cdfa77d0fc82068346e9731c7e884097160603872686e570f07"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e586acc0b450cd21cbf0db6bae386237011b75260a3adceddc4be15334689a9a"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22e7944ffc1f0099a79ff468ee9630c73f8c7835cd76fdb57ef7320e6a409df4"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fa30af36bd8e50686846bdace37cf6707bdd044e5cb6e1109acbad3277232e04"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:440215347b11914ae707981b9a57ab9c7b6983ab0babde07063c6ee75c0dc6e7"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:314c4038dc5e6a52991570f50edb2f08c339debdf8cea68ac355b32c4174e820"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5acb1d386337415f74f4d1955c4ce8d0201978c162927d07df8eb0692b2d8533"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a789e12fbdefaffb7b8ac67f9d8f22ba17a3050ceaaa635cd1cc4645773a4b1e"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:860aa9b8a888e5913bd70d819306749e5eb488e6b99cd6c47beb701b22bdecf5"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:5ebc65ea033ef0281368217fbf59f5cb05b338ac4dd23d60959c7afcd79a60a0"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9fef18ab0386ef6a9ac7bad7e43ded42c83ff7ad412f950633854f90d59afa8"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c134ba2f0571d0b68b83f6972e2307a55a5a849e7dac8505c715c531d2a8795"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:385680812e7e18af200bb9b4a49777418c32422d05ad5a8eb85144c4a285907b"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eec91cd87c59fb32ec49eb722f375bd58f4be790cae11c1b70fac3ee4f00da0"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4386bffeca23c4b69ad50a36211f75b35a4deb6210bdca112ac3043deb7e494a"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dfbcf1661d7146d7698da4b86e7f04814221081e9fe154183e34f4c5f5fa3bf8"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:04a1094a5aa1daa34a6b57c865b25f691848c61583fb22722a4df5699f6bf74c"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5b0ec85b9045bd49dd3a3493a5e7ddfd31c36a2a60da387c419fa04abcaecb23"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0a15b90129765b705eb2039062a6daf4d22c4e28d1a54fa260892e8c3ae6e157"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:bb8f6d7acd67a67d6fedd361ad2958ff0539445ef51cbe8cd288db4306503cd0"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd69b15374bef7e4b4440612915315cc42e8575fcda2a3d7586a0d88192d0c88"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc00f8110db6898360c53c812872662e077eaf9c75515d53ecc65d886eec209a"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:83a44e8b40655d0ba565a5c3d1365d27e3e6778ae2a05b69124db9e471255c4a"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1a3604e9fbc06b788041b2a8b78f75c243021e0f512447806a6d37ee5214905d"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:92c307ae7accebd06cbae4729f0ba9fa724df5f7d91a0964b1b972a22baa482b"}, - {file = "pendulum-3.0.0.tar.gz", hash = "sha256:5d034998dea404ec31fae27af6b22cff1708f830a1ed7353be4d1019bb9f584e"}, -] - -[package.dependencies] -python-dateutil = ">=2.6" -tzdata = ">=2020.1" - -[package.extras] -test = ["time-machine (>=2.6.0)"] - [[package]] name = "platformdirs" version = "4.3.6" @@ -4431,6 +4310,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, + {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, @@ -4439,6 +4319,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, + {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, @@ -4447,6 +4328,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, + {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, @@ -4455,6 +4337,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, + {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, @@ -4463,6 +4346,7 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, + {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, @@ -5206,4 +5090,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "f2d3920001616ff9b66353b61d20dadc358787aa154c31fcbd8f4baf45c36498" +content-hash = "19e7c4654f280256e53757abd04cb63938d34ae4d8305a77f21e75c891b5376d" diff --git a/pyproject.toml b/pyproject.toml index ff3156c743..9cb926c96e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ google-api-python-client = "2.159.0" google-auth-httplib2 = ">=0.1,<0.3" jsonschema = "4.23.0" kubernetes = "31.0.0" -microsoft-kiota-abstractions = "1.6.8" +microsoft-kiota-abstractions = "1.9.1" msgraph-sdk = "1.18.0" numpy = "2.0.2" pandas = "2.2.3" From 1256c040e9d4bcb02633b86f1523608e3f0804a3 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Fri, 31 Jan 2025 12:32:39 +0100 Subject: [PATCH 08/24] fix: microsoft365 mutelist (#6724) --- prowler/lib/check/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prowler/lib/check/models.py b/prowler/lib/check/models.py index 2aca0e83c1..928d68f075 100644 --- a/prowler/lib/check/models.py +++ b/prowler/lib/check/models.py @@ -541,6 +541,7 @@ class Check_Report_Microsoft365(Check_Report): resource_name: str resource_id: str + tenant_id: str location: str def __init__(self, metadata: Dict, resource: Any) -> None: @@ -555,6 +556,7 @@ class Check_Report_Microsoft365(Check_Report): resource, "name", getattr(resource, "resource_name", "") ) self.resource_id = getattr(resource, "id", getattr(resource, "resource_id", "")) + self.tenant_id = getattr(resource, "tenant_id", "") self.location = getattr(resource, "location", "global") From 763130f253cb119d30b413f0b917abfbe427193b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20Poyatos?= Date: Fri, 31 Jan 2025 14:45:08 +0100 Subject: [PATCH 09/24] fix(celery): Kill celery worker process after every task to release memory (#6761) --- api/docker-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/docker-entrypoint.sh b/api/docker-entrypoint.sh index 62ceb69a0f..893d94af21 100755 --- a/api/docker-entrypoint.sh +++ b/api/docker-entrypoint.sh @@ -28,7 +28,7 @@ start_prod_server() { start_worker() { echo "Starting the worker..." - poetry run python -m celery -A config.celery worker -l "${DJANGO_LOGGING_LEVEL:-info}" -Q celery,scans -E + poetry run python -m celery -A config.celery worker -l "${DJANGO_LOGGING_LEVEL:-info}" -Q celery,scans -E --max-tasks-per-child 1 } start_worker_beat() { From ef6af8e84de0e68285a5b2a297ea02939a82cea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20Poyatos?= Date: Mon, 3 Feb 2025 11:08:27 +0100 Subject: [PATCH 10/24] feat(schedules): Rework daily schedule to always show the next scan (#6700) --- api/pyproject.toml | 2 +- .../0008_daily_scheduled_tasks_update.py | 64 ++++++++++++++++ api/src/backend/api/models.py | 4 + api/src/backend/api/specs/v1.yaml | 2 +- api/src/backend/api/v1/urls.py | 4 - api/src/backend/api/v1/views.py | 2 +- api/src/backend/config/django/devel.py | 6 -- api/src/backend/tasks/beat.py | 55 +++++++++----- api/src/backend/tasks/jobs/scan.py | 7 +- api/src/backend/tasks/tasks.py | 49 +++++++----- api/src/backend/tasks/tests/test_beat.py | 4 + api/src/backend/tasks/tests/test_utils.py | 76 +++++++++++++++++++ api/src/backend/tasks/utils.py | 26 +++++++ 13 files changed, 249 insertions(+), 52 deletions(-) create mode 100644 api/src/backend/api/migrations/0008_daily_scheduled_tasks_update.py create mode 100644 api/src/backend/tasks/tests/test_utils.py create mode 100644 api/src/backend/tasks/utils.py diff --git a/api/pyproject.toml b/api/pyproject.toml index 3c7338eb53..2cccb9ddb3 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -8,7 +8,7 @@ description = "Prowler's API (Django/DRF)" license = "Apache-2.0" name = "prowler-api" package-mode = false -version = "1.3.2" +version = "1.4.0" [tool.poetry.dependencies] celery = {extras = ["pytest"], version = "^5.4.0"} diff --git a/api/src/backend/api/migrations/0008_daily_scheduled_tasks_update.py b/api/src/backend/api/migrations/0008_daily_scheduled_tasks_update.py new file mode 100644 index 0000000000..7f059ea2b8 --- /dev/null +++ b/api/src/backend/api/migrations/0008_daily_scheduled_tasks_update.py @@ -0,0 +1,64 @@ +import json +from datetime import datetime, timedelta, timezone + +import django.db.models.deletion +from django.db import migrations, models +from django_celery_beat.models import PeriodicTask + +from api.db_utils import rls_transaction +from api.models import Scan, StateChoices + + +def migrate_daily_scheduled_scan_tasks(apps, schema_editor): + for daily_scheduled_scan_task in PeriodicTask.objects.filter( + task="scan-perform-scheduled" + ): + task_kwargs = json.loads(daily_scheduled_scan_task.kwargs) + tenant_id = task_kwargs["tenant_id"] + provider_id = task_kwargs["provider_id"] + + current_time = datetime.now(timezone.utc) + scheduled_time_today = datetime.combine( + current_time.date(), + daily_scheduled_scan_task.start_time.time(), + tzinfo=timezone.utc, + ) + + if current_time < scheduled_time_today: + next_scan_date = scheduled_time_today + else: + next_scan_date = scheduled_time_today + timedelta(days=1) + + with rls_transaction(tenant_id): + Scan.objects.create( + tenant_id=tenant_id, + name="Daily scheduled scan", + provider_id=provider_id, + trigger=Scan.TriggerChoices.SCHEDULED, + state=StateChoices.SCHEDULED, + scheduled_at=next_scan_date, + scheduler_task_id=daily_scheduled_scan_task.id, + ) + + +class Migration(migrations.Migration): + atomic = False + + dependencies = [ + ("api", "0007_scan_and_scan_summaries_indexes"), + ("django_celery_beat", "0019_alter_periodictasks_options"), + ] + + operations = [ + migrations.AddField( + model_name="scan", + name="scheduler_task", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="django_celery_beat.periodictask", + ), + ), + migrations.RunPython(migrate_daily_scheduled_scan_tasks), + ] diff --git a/api/src/backend/api/models.py b/api/src/backend/api/models.py index 09014321cb..ef982be8c7 100644 --- a/api/src/backend/api/models.py +++ b/api/src/backend/api/models.py @@ -11,6 +11,7 @@ from django.core.validators import MinLengthValidator from django.db import models from django.db.models import Q from django.utils.translation import gettext_lazy as _ +from django_celery_beat.models import PeriodicTask from django_celery_results.models import TaskResult from psqlextra.manager import PostgresManager from psqlextra.models import PostgresPartitionedModel @@ -410,6 +411,9 @@ class Scan(RowLevelSecurityProtectedModel): started_at = models.DateTimeField(null=True, blank=True) completed_at = models.DateTimeField(null=True, blank=True) next_scan_at = models.DateTimeField(null=True, blank=True) + scheduler_task = models.ForeignKey( + PeriodicTask, on_delete=models.CASCADE, null=True, blank=True + ) # TODO: mutelist foreign key class Meta(RowLevelSecurityProtectedModel.Meta): diff --git a/api/src/backend/api/specs/v1.yaml b/api/src/backend/api/specs/v1.yaml index 7448843b54..ab25ef0e27 100644 --- a/api/src/backend/api/specs/v1.yaml +++ b/api/src/backend/api/specs/v1.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: title: Prowler API - version: 1.3.2 + version: 1.4.0 description: |- Prowler API specification. diff --git a/api/src/backend/api/v1/urls.py b/api/src/backend/api/v1/urls.py index 6b230960e1..4ff587d913 100644 --- a/api/src/backend/api/v1/urls.py +++ b/api/src/backend/api/v1/urls.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.urls import include, path from drf_spectacular.views import SpectacularRedocView from rest_framework_nested import routers @@ -113,6 +112,3 @@ urlpatterns = [ path("schema", SchemaView.as_view(), name="schema"), path("docs", SpectacularRedocView.as_view(url_name="schema"), name="docs"), ] - -if settings.DEBUG: - urlpatterns += [path("silk/", include("silk.urls", namespace="silk"))] diff --git a/api/src/backend/api/v1/views.py b/api/src/backend/api/v1/views.py index 3ecb6474fb..b11626c829 100644 --- a/api/src/backend/api/v1/views.py +++ b/api/src/backend/api/v1/views.py @@ -193,7 +193,7 @@ class SchemaView(SpectacularAPIView): def get(self, request, *args, **kwargs): spectacular_settings.TITLE = "Prowler API" - spectacular_settings.VERSION = "1.3.2" + spectacular_settings.VERSION = "1.4.0" spectacular_settings.DESCRIPTION = ( "Prowler API specification.\n\nThis file is auto-generated." ) diff --git a/api/src/backend/config/django/devel.py b/api/src/backend/config/django/devel.py index 6ee92a6ecb..825e1ce36a 100644 --- a/api/src/backend/config/django/devel.py +++ b/api/src/backend/config/django/devel.py @@ -37,9 +37,3 @@ REST_FRAMEWORK["DEFAULT_FILTER_BACKENDS"] = tuple( # noqa: F405 ) + ("api.filters.CustomDjangoFilterBackend",) SECRETS_ENCRYPTION_KEY = "ZMiYVo7m4Fbe2eXXPyrwxdJss2WSalXSv3xHBcJkPl0=" - -MIDDLEWARE += [ # noqa: F405 - "silk.middleware.SilkyMiddleware", -] - -INSTALLED_APPS += ["silk"] # noqa: F405 diff --git a/api/src/backend/tasks/beat.py b/api/src/backend/tasks/beat.py index 81dc04abba..6cd8d7a9ce 100644 --- a/api/src/backend/tasks/beat.py +++ b/api/src/backend/tasks/beat.py @@ -5,10 +5,14 @@ from django_celery_beat.models import IntervalSchedule, PeriodicTask from rest_framework_json_api.serializers import ValidationError from tasks.tasks import perform_scheduled_scan_task -from api.models import Provider +from api.db_utils import rls_transaction +from api.models import Provider, Scan, StateChoices def schedule_provider_scan(provider_instance: Provider): + tenant_id = str(provider_instance.tenant_id) + provider_id = str(provider_instance.id) + schedule, _ = IntervalSchedule.objects.get_or_create( every=24, period=IntervalSchedule.HOURS, @@ -17,23 +21,9 @@ def schedule_provider_scan(provider_instance: Provider): # Create a unique name for the periodic task task_name = f"scan-perform-scheduled-{provider_instance.id}" - # Schedule the task - _, created = PeriodicTask.objects.get_or_create( - interval=schedule, - name=task_name, - task="scan-perform-scheduled", - kwargs=json.dumps( - { - "tenant_id": str(provider_instance.tenant_id), - "provider_id": str(provider_instance.id), - } - ), - one_off=False, - defaults={ - "start_time": datetime.now(timezone.utc) + timedelta(hours=24), - }, - ) - if not created: + if PeriodicTask.objects.filter( + interval=schedule, name=task_name, task="scan-perform-scheduled" + ).exists(): raise ValidationError( [ { @@ -45,9 +35,36 @@ def schedule_provider_scan(provider_instance: Provider): ] ) + with rls_transaction(tenant_id): + scheduled_scan = Scan.objects.create( + tenant_id=tenant_id, + name="Daily scheduled scan", + provider_id=provider_id, + trigger=Scan.TriggerChoices.SCHEDULED, + state=StateChoices.AVAILABLE, + scheduled_at=datetime.now(timezone.utc), + ) + + # Schedule the task + periodic_task_instance = PeriodicTask.objects.create( + interval=schedule, + name=task_name, + task="scan-perform-scheduled", + kwargs=json.dumps( + { + "tenant_id": tenant_id, + "provider_id": provider_id, + } + ), + one_off=False, + start_time=datetime.now(timezone.utc) + timedelta(hours=24), + ) + scheduled_scan.scheduler_task_id = periodic_task_instance.id + scheduled_scan.save() + return perform_scheduled_scan_task.apply_async( kwargs={ "tenant_id": str(provider_instance.tenant_id), - "provider_id": str(provider_instance.id), + "provider_id": provider_id, }, ) diff --git a/api/src/backend/tasks/jobs/scan.py b/api/src/backend/tasks/jobs/scan.py index b11366a859..ad11079ef0 100644 --- a/api/src/backend/tasks/jobs/scan.py +++ b/api/src/backend/tasks/jobs/scan.py @@ -245,8 +245,11 @@ def perform_prowler_scan( status = FindingStatus[finding.status] delta = _create_finding_delta(last_status, status) - # For the findings prior to the change, when a first finding is found with delta!="new" it will be assigned a current date as first_seen_at and the successive findings with the same UID will always get the date of the previous finding. - # For new findings, when a finding (delta="new") is found for the first time, the first_seen_at attribute will be assigned the current date, the following findings will get that date. + # For the findings prior to the change, when a first finding is found with delta!="new" it will be + # assigned a current date as first_seen_at and the successive findings with the same UID will + # always get the date of the previous finding. + # For new findings, when a finding (delta="new") is found for the first time, the first_seen_at + # attribute will be assigned the current date, the following findings will get that date. if not last_first_seen_at: last_first_seen_at = datetime.now(tz=timezone.utc) diff --git a/api/src/backend/tasks/tasks.py b/api/src/backend/tasks/tasks.py index 1f87e0fdeb..792900c4e7 100644 --- a/api/src/backend/tasks/tasks.py +++ b/api/src/backend/tasks/tasks.py @@ -1,15 +1,14 @@ -from datetime import datetime, timedelta, timezone - from celery import shared_task from config.celery import RLSTask from django_celery_beat.models import PeriodicTask from tasks.jobs.connection import check_provider_connection from tasks.jobs.deletion import delete_provider, delete_tenant from tasks.jobs.scan import aggregate_findings, perform_prowler_scan +from tasks.utils import get_next_execution_datetime from api.db_utils import rls_transaction from api.decorators import set_tenant -from api.models import Provider, Scan +from api.models import Scan, StateChoices @shared_task(base=RLSTask, name="provider-connection-check") @@ -100,28 +99,42 @@ def perform_scheduled_scan_task(self, tenant_id: str, provider_id: str): task_id = self.request.id with rls_transaction(tenant_id): - provider_instance = Provider.objects.get(pk=provider_id) periodic_task_instance = PeriodicTask.objects.get( name=f"scan-perform-scheduled-{provider_id}" ) - next_scan_date = datetime.combine( - datetime.now(timezone.utc), periodic_task_instance.start_time.time() - ) + timedelta(hours=24) - - scan_instance = Scan.objects.create( + next_scan_datetime = get_next_execution_datetime(task_id, provider_id) + scan_instance, _ = Scan.objects.get_or_create( tenant_id=tenant_id, - name="Daily scheduled scan", - provider=provider_instance, + provider_id=provider_id, trigger=Scan.TriggerChoices.SCHEDULED, - next_scan_at=next_scan_date, - task_id=task_id, + state__in=(StateChoices.SCHEDULED, StateChoices.AVAILABLE), + scheduler_task_id=periodic_task_instance.id, + defaults={"state": StateChoices.SCHEDULED}, ) - result = perform_prowler_scan( - tenant_id=tenant_id, - scan_id=str(scan_instance.id), - provider_id=provider_id, - ) + scan_instance.task_id = task_id + scan_instance.save() + + try: + result = perform_prowler_scan( + tenant_id=tenant_id, + scan_id=str(scan_instance.id), + provider_id=provider_id, + ) + except Exception as e: + raise e + finally: + with rls_transaction(tenant_id): + Scan.objects.get_or_create( + tenant_id=tenant_id, + name="Daily scheduled scan", + provider_id=provider_id, + trigger=Scan.TriggerChoices.SCHEDULED, + state=StateChoices.SCHEDULED, + scheduled_at=next_scan_datetime, + scheduler_task_id=periodic_task_instance.id, + ) + perform_scan_summary_task.apply_async( kwargs={ "tenant_id": tenant_id, diff --git a/api/src/backend/tasks/tests/test_beat.py b/api/src/backend/tasks/tests/test_beat.py index 78b5acb039..6e5c6fdf91 100644 --- a/api/src/backend/tasks/tests/test_beat.py +++ b/api/src/backend/tasks/tests/test_beat.py @@ -6,6 +6,8 @@ from django_celery_beat.models import IntervalSchedule, PeriodicTask from rest_framework_json_api.serializers import ValidationError from tasks.beat import schedule_provider_scan +from api.models import Scan + @pytest.mark.django_db class TestScheduleProviderScan: @@ -15,9 +17,11 @@ class TestScheduleProviderScan: with patch( "tasks.tasks.perform_scheduled_scan_task.apply_async" ) as mock_apply_async: + assert Scan.all_objects.count() == 0 result = schedule_provider_scan(provider_instance) assert result is not None + assert Scan.all_objects.count() == 1 mock_apply_async.assert_called_once_with( kwargs={ diff --git a/api/src/backend/tasks/tests/test_utils.py b/api/src/backend/tasks/tests/test_utils.py new file mode 100644 index 0000000000..619626af9e --- /dev/null +++ b/api/src/backend/tasks/tests/test_utils.py @@ -0,0 +1,76 @@ +from datetime import datetime, timedelta, timezone +from unittest.mock import patch + +import pytest +from django_celery_beat.models import IntervalSchedule, PeriodicTask +from django_celery_results.models import TaskResult +from tasks.utils import get_next_execution_datetime + + +@pytest.mark.django_db +class TestGetNextExecutionDatetime: + @pytest.fixture + def setup_periodic_task(self, db): + # Create a periodic task with an hourly interval + interval = IntervalSchedule.objects.create( + every=1, period=IntervalSchedule.HOURS + ) + periodic_task = PeriodicTask.objects.create( + name="scan-perform-scheduled-123", + task="scan-perform-scheduled", + interval=interval, + ) + return periodic_task + + @pytest.fixture + def setup_task_result(self, db): + # Create a task result record + task_result = TaskResult.objects.create( + task_id="abc123", + task_name="scan-perform-scheduled", + status="SUCCESS", + date_created=datetime.now(timezone.utc) - timedelta(hours=1), + result="Success", + ) + return task_result + + def test_get_next_execution_datetime_success( + self, setup_task_result, setup_periodic_task + ): + task_result = setup_task_result + periodic_task = setup_periodic_task + + # Mock periodic_task_name on TaskResult + with patch.object( + TaskResult, "periodic_task_name", return_value=periodic_task.name + ): + next_execution = get_next_execution_datetime( + task_id=task_result.task_id, provider_id="123" + ) + + expected_time = task_result.date_created + timedelta(hours=1) + assert next_execution == expected_time + + def test_get_next_execution_datetime_fallback_to_provider_id( + self, setup_task_result, setup_periodic_task + ): + task_result = setup_task_result + + # Simulate the case where `periodic_task_name` is missing + with patch.object(TaskResult, "periodic_task_name", return_value=None): + next_execution = get_next_execution_datetime( + task_id=task_result.task_id, provider_id="123" + ) + + expected_time = task_result.date_created + timedelta(hours=1) + assert next_execution == expected_time + + def test_get_next_execution_datetime_periodic_task_does_not_exist( + self, setup_task_result + ): + task_result = setup_task_result + + with pytest.raises(PeriodicTask.DoesNotExist): + get_next_execution_datetime( + task_id=task_result.task_id, provider_id="nonexistent" + ) diff --git a/api/src/backend/tasks/utils.py b/api/src/backend/tasks/utils.py new file mode 100644 index 0000000000..5ac78a03ab --- /dev/null +++ b/api/src/backend/tasks/utils.py @@ -0,0 +1,26 @@ +from datetime import datetime, timedelta, timezone + +from django_celery_beat.models import PeriodicTask +from django_celery_results.models import TaskResult + + +def get_next_execution_datetime(task_id: int, provider_id: str) -> datetime: + task_instance = TaskResult.objects.get(task_id=task_id) + try: + periodic_task_instance = PeriodicTask.objects.get( + name=task_instance.periodic_task_name + ) + except PeriodicTask.DoesNotExist: + periodic_task_instance = PeriodicTask.objects.get( + name=f"scan-perform-scheduled-{provider_id}" + ) + + interval = periodic_task_instance.interval + + current_scheduled_time = datetime.combine( + datetime.now(timezone.utc).date(), + task_instance.date_created.time(), + tzinfo=timezone.utc, + ) + + return current_scheduled_time + timedelta(**{interval.period: interval.every}) From 6a954267499d55076c11d109f637dfea4cd511af Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 3 Feb 2025 11:51:07 +0100 Subject: [PATCH 11/24] fix(findings): order findings by inserted_at DESC (#6782) --- ui/app/(prowler)/findings/page.tsx | 7 +++---- ui/app/(prowler)/page.tsx | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ui/app/(prowler)/findings/page.tsx b/ui/app/(prowler)/findings/page.tsx index 0d35cac8c4..f619bccde9 100644 --- a/ui/app/(prowler)/findings/page.tsx +++ b/ui/app/(prowler)/findings/page.tsx @@ -26,11 +26,10 @@ export default async function Findings({ searchParams: SearchParamsProps; }) { const searchParamsKey = JSON.stringify(searchParams || {}); - const defaultSort = "severity,status"; - const sort = searchParams.sort?.toString() || defaultSort; + const sort = searchParams.sort?.toString(); // Make sure the sort is correctly encoded - const encodedSort = sort.replace(/^\+/, ""); + const encodedSort = sort?.replace(/^\+/, ""); // Extract all filter parameters and combine with default filters const defaultFilters = { @@ -137,7 +136,7 @@ const SSRDataTable = async ({ searchParams: SearchParamsProps; }) => { const page = parseInt(searchParams.page?.toString() || "1", 10); - const defaultSort = "severity,status"; + const defaultSort = "severity,status,-inserted_at"; const sort = searchParams.sort?.toString() || defaultSort; // Make sure the sort is correctly encoded diff --git a/ui/app/(prowler)/page.tsx b/ui/app/(prowler)/page.tsx index ff2ab3d28c..c6e0748362 100644 --- a/ui/app/(prowler)/page.tsx +++ b/ui/app/(prowler)/page.tsx @@ -129,7 +129,7 @@ const SSRFindingsBySeverity = async ({ const SSRDataNewFindingsTable = async () => { const page = 1; - const sort = "severity,updated_at"; + const sort = "severity,-inserted_at"; const defaultFilters = { "filter[status__in]": "FAIL", From bb4685cf90aa07f8d4523cbcd98067ab8a9a9a53 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 3 Feb 2025 15:20:18 +0100 Subject: [PATCH 12/24] fix(findings): remove default status filtering (#6784) --- ui/app/(prowler)/findings/page.tsx | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/ui/app/(prowler)/findings/page.tsx b/ui/app/(prowler)/findings/page.tsx index f619bccde9..39d2ad7846 100644 --- a/ui/app/(prowler)/findings/page.tsx +++ b/ui/app/(prowler)/findings/page.tsx @@ -31,15 +31,14 @@ export default async function Findings({ // Make sure the sort is correctly encoded const encodedSort = sort?.replace(/^\+/, ""); - // Extract all filter parameters and combine with default filters - const defaultFilters = { - "filter[status__in]": "FAIL, PASS", - }; - const filters: Record = { - ...defaultFilters, ...Object.fromEntries( - Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")), + Object.entries(searchParams) + .filter(([key]) => key.startsWith("filter[")) + .map(([key, value]) => [ + key, + Array.isArray(value) ? value.join(",") : value?.toString() || "", + ]), ), }; @@ -142,15 +141,14 @@ const SSRDataTable = async ({ // Make sure the sort is correctly encoded const encodedSort = sort.replace(/^\+/, ""); - // Extract all filter parameters and combine with default filters - const defaultFilters = { - "filter[status__in]": "FAIL, PASS", - }; - const filters: Record = { - ...defaultFilters, ...Object.fromEntries( - Object.entries(searchParams).filter(([key]) => key.startsWith("filter[")), + Object.entries(searchParams) + .filter(([key]) => key.startsWith("filter[")) + .map(([key, value]) => [ + key, + Array.isArray(value) ? value.join(",") : value?.toString() || "", + ]), ), }; From c0d7c9fc7d8300a7266df6fa24aa7e8a519f8295 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 09:27:17 -0500 Subject: [PATCH 13/24] chore(deps): bump google-api-python-client from 2.159.0 to 2.160.0 (#6720) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index cbb2504f70..0772ec464a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1692,13 +1692,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.159.0" +version = "2.160.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google_api_python_client-2.159.0-py2.py3-none-any.whl", hash = "sha256:baef0bb631a60a0bd7c0bf12a5499e3a40cd4388484de7ee55c1950bf820a0cf"}, - {file = "google_api_python_client-2.159.0.tar.gz", hash = "sha256:55197f430f25c907394b44fa078545ffef89d33fd4dca501b7db9f0d8e224bd6"}, + {file = "google_api_python_client-2.160.0-py2.py3-none-any.whl", hash = "sha256:63d61fb3e4cf3fb31a70a87f45567c22f6dfe87bbfa27252317e3e2c42900db4"}, + {file = "google_api_python_client-2.160.0.tar.gz", hash = "sha256:a8ccafaecfa42d15d5b5c3134ced8de08380019717fc9fb1ed510ca58eca3b7e"}, ] [package.dependencies] @@ -5090,4 +5090,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "19e7c4654f280256e53757abd04cb63938d34ae4d8305a77f21e75c891b5376d" +content-hash = "c2620b4dedb78560320e7b977e3e481a1179cc4317e3b862ac869de83af868f2" diff --git a/pyproject.toml b/pyproject.toml index 9cb926c96e..a747c0f208 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ cryptography = "43.0.1" dash = "2.18.2" dash-bootstrap-components = "1.6.0" detect-secrets = "1.5.0" -google-api-python-client = "2.159.0" +google-api-python-client = "2.160.0" google-auth-httplib2 = ">=0.1,<0.3" jsonschema = "4.23.0" kubernetes = "31.0.0" From 867b371522387071859a77b16fa74f7849a3ff33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Mon, 3 Feb 2025 15:28:04 +0100 Subject: [PATCH 14/24] fix(cis_2.0_aws): add checks to needed reqs (#6694) --- prowler/compliance/aws/cis_2.0_aws.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/prowler/compliance/aws/cis_2.0_aws.json b/prowler/compliance/aws/cis_2.0_aws.json index 09f2ebe08f..1d12e296d5 100644 --- a/prowler/compliance/aws/cis_2.0_aws.json +++ b/prowler/compliance/aws/cis_2.0_aws.json @@ -303,7 +303,9 @@ { "Id": "1.22", "Description": "Ensure access to AWSCloudShellFullAccess is restricted", - "Checks": [], + "Checks": [ + "iam_policy_cloudshell_admin_not_attached" + ], "Attributes": [ { "Section": "1. Identity and Access Management", @@ -492,7 +494,8 @@ "Id": "2.1.2", "Description": "Ensure MFA Delete is enabled on S3 buckets", "Checks": [ - "s3_bucket_no_mfa_delete" + "s3_bucket_no_mfa_delete", + "cloudtrail_bucket_requires_mfa_delete" ], "Attributes": [ { @@ -581,7 +584,8 @@ "Id": "2.3.1", "Description": "Ensure that encryption is enabled for RDS Instances", "Checks": [ - "rds_instance_storage_encrypted" + "rds_instance_storage_encrypted", + "rds_instance_transport_encrypted" ], "Attributes": [ { @@ -1347,7 +1351,8 @@ "Id": "5.6", "Description": "Ensure that EC2 Metadata Service only allows IMDSv2", "Checks": [ - "ec2_instance_imdsv2_enabled" + "ec2_instance_imdsv2_enabled", + "ec2_instance_account_imdsv2_enabled" ], "Attributes": [ { From db8b4727295cbb34b2637331ea89f02da35e609d Mon Sep 17 00:00:00 2001 From: secretcod3r <101349794+secretcod3r@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:29:08 +0200 Subject: [PATCH 15/24] fix(gcp): fix wrong provider value in check (#6691) --- .../compute_public_address_shodan.metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler/providers/gcp/services/compute/compute_public_address_shodan/compute_public_address_shodan.metadata.json b/prowler/providers/gcp/services/compute/compute_public_address_shodan/compute_public_address_shodan.metadata.json index 5c19d5d1f7..d897b42a1f 100644 --- a/prowler/providers/gcp/services/compute/compute_public_address_shodan/compute_public_address_shodan.metadata.json +++ b/prowler/providers/gcp/services/compute/compute_public_address_shodan/compute_public_address_shodan.metadata.json @@ -1,5 +1,5 @@ { - "Provider": "compute", + "Provider": "gcp", "CheckID": "compute_public_address_shodan", "CheckTitle": "Check if any of the Public Addresses are in Shodan (requires Shodan API KEY).", "CheckType": [ From 1b6f9332f18fddf252ec212f8c05b714dc4733d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 09:35:53 -0500 Subject: [PATCH 16/24] chore(deps): bump trufflesecurity/trufflehog from 3.88.2 to 3.88.4 (#6760) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/find-secrets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/find-secrets.yml b/.github/workflows/find-secrets.yml index b39eb8cdc5..0eb7bbd6fe 100644 --- a/.github/workflows/find-secrets.yml +++ b/.github/workflows/find-secrets.yml @@ -11,7 +11,7 @@ jobs: with: fetch-depth: 0 - name: TruffleHog OSS - uses: trufflesecurity/trufflehog@v3.88.2 + uses: trufflesecurity/trufflehog@v3.88.4 with: path: ./ base: ${{ github.event.repository.default_branch }} From 8e784a5b6d6373e29b3ba75d03d672c9e460aaf8 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Mon, 3 Feb 2025 16:08:47 +0100 Subject: [PATCH 17/24] feat(scans): show scan details right after launch (#6791) --- ui/app/(prowler)/scans/page.tsx | 3 --- ui/components/scans/index.ts | 1 - ui/components/scans/scan-warning-bar.tsx | 18 ------------------ ui/components/scans/table/scan-detail.tsx | 8 +++----- .../scans/table/scans/column-get-scans.tsx | 8 ++++---- 5 files changed, 7 insertions(+), 31 deletions(-) delete mode 100644 ui/components/scans/scan-warning-bar.tsx diff --git a/ui/app/(prowler)/scans/page.tsx b/ui/app/(prowler)/scans/page.tsx index 9781627118..db38bd4e97 100644 --- a/ui/app/(prowler)/scans/page.tsx +++ b/ui/app/(prowler)/scans/page.tsx @@ -8,7 +8,6 @@ import { ButtonRefreshData, NoProvidersAdded, NoProvidersConnected, - ScanWarningBar, } from "@/components/scans"; import { LaunchScanWorkflow } from "@/components/scans/launch-workflow"; import { SkeletonTableScans } from "@/components/scans/table"; @@ -73,8 +72,6 @@ export default async function Scans({
- - )} diff --git a/ui/components/scans/index.ts b/ui/components/scans/index.ts index 98157857ab..aa42edfe3c 100644 --- a/ui/components/scans/index.ts +++ b/ui/components/scans/index.ts @@ -2,4 +2,3 @@ export * from "./button-refresh-data"; export * from "./link-to-findings-from-scan"; export * from "./no-providers-added"; export * from "./no-providers-connected"; -export * from "./scan-warning-bar"; diff --git a/ui/components/scans/scan-warning-bar.tsx b/ui/components/scans/scan-warning-bar.tsx deleted file mode 100644 index 4c9acbb978..0000000000 --- a/ui/components/scans/scan-warning-bar.tsx +++ /dev/null @@ -1,18 +0,0 @@ -"use client"; - -import { InfoIcon } from "../icons"; - -export const ScanWarningBar = () => { - return ( -
- -
- Waiting for Your Scan to Show Up? -

- It may take a few minutes for the scan to appear on the table and be - displayed. -

-
-
- ); -}; diff --git a/ui/components/scans/table/scan-detail.tsx b/ui/components/scans/table/scan-detail.tsx index 6e9c3e288c..7307ff53f0 100644 --- a/ui/components/scans/table/scan-detail.tsx +++ b/ui/components/scans/table/scan-detail.tsx @@ -117,11 +117,9 @@ export const ScanDetail = ({ - {scan.next_scan_at && ( - - - - )} + + + diff --git a/ui/components/scans/table/scans/column-get-scans.tsx b/ui/components/scans/table/scans/column-get-scans.tsx index 55087a0e66..72a70f9311 100644 --- a/ui/components/scans/table/scans/column-get-scans.tsx +++ b/ui/components/scans/table/scans/column-get-scans.tsx @@ -123,13 +123,13 @@ export const ColumnGetScans: ColumnDef[] = [ }, }, { - accessorKey: "next_scan_at", - header: "Next scan", + accessorKey: "scheduled_at", + header: "Scheduled at", cell: ({ row }) => { const { - attributes: { next_scan_at }, + attributes: { scheduled_at }, } = getScanData(row); - return ; + return ; }, }, { From a73ffe5642efe946440ab14af7d1c13d9f9dc9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Mon, 3 Feb 2025 16:32:10 +0100 Subject: [PATCH 18/24] fix(cis_1.4_aws): add checks to needed reqs (#6696) Co-authored-by: Sergio Garcia --- prowler/compliance/aws/cis_1.4_aws.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prowler/compliance/aws/cis_1.4_aws.json b/prowler/compliance/aws/cis_1.4_aws.json index da77feb991..584ca284c6 100644 --- a/prowler/compliance/aws/cis_1.4_aws.json +++ b/prowler/compliance/aws/cis_1.4_aws.json @@ -584,7 +584,8 @@ "Id": "2.3.1", "Description": "Ensure that encryption is enabled for RDS Instances", "Checks": [ - "rds_instance_storage_encrypted" + "rds_instance_storage_encrypted", + "rds_instance_transport_encrypted" ], "Attributes": [ { From 072828512a34433a9229daf022dc5b009dadf8c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Mon, 3 Feb 2025 16:32:20 +0100 Subject: [PATCH 19/24] fix(cis_1.5_aws): add checks to needed reqs (#6695) Co-authored-by: Sergio Garcia --- prowler/compliance/aws/cis_1.5_aws.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prowler/compliance/aws/cis_1.5_aws.json b/prowler/compliance/aws/cis_1.5_aws.json index 6d5283b81c..90ca3742e4 100644 --- a/prowler/compliance/aws/cis_1.5_aws.json +++ b/prowler/compliance/aws/cis_1.5_aws.json @@ -584,7 +584,8 @@ "Id": "2.3.1", "Description": "Ensure that encryption is enabled for RDS Instances", "Checks": [ - "rds_instance_storage_encrypted" + "rds_instance_storage_encrypted", + "rds_instance_transport_encrypted" ], "Attributes": [ { From 3858cccc417aa6ca33f4926985d727c82c87e366 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 10:32:42 -0500 Subject: [PATCH 20/24] chore(deps-dev): bump pylint from 3.3.3 to 3.3.4 (#6721) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 10 +++++----- pyproject.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0772ec464a..ce20b23af4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3635,13 +3635,13 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pylint" -version = "3.3.3" +version = "3.3.4" description = "python code static checker" optional = false python-versions = ">=3.9.0" files = [ - {file = "pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183"}, - {file = "pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a"}, + {file = "pylint-3.3.4-py3-none-any.whl", hash = "sha256:289e6a1eb27b453b08436478391a48cd53bb0efb824873f949e709350f3de018"}, + {file = "pylint-3.3.4.tar.gz", hash = "sha256:74ae7a38b177e69a9b525d0794bd8183820bfa7eb68cc1bee6e8ed22a42be4ce"}, ] [package.dependencies] @@ -3652,7 +3652,7 @@ dill = [ {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +isort = ">=4.2.5,<5.13.0 || >5.13.0,<7" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -5090,4 +5090,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "c2620b4dedb78560320e7b977e3e481a1179cc4317e3b862ac869de83af868f2" +content-hash = "862dc654b14d6b85f9d8951bbf476b3d9f2fe60b5af1de0e7a69337178244584" diff --git a/pyproject.toml b/pyproject.toml index a747c0f208..c881814f81 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,7 @@ mock = "5.1.0" moto = {extras = ["all"], version = "5.0.27"} openapi-schema-validator = "0.6.3" openapi-spec-validator = "0.7.1" -pylint = "3.3.3" +pylint = "3.3.4" pytest = "8.3.4" pytest-cov = "6.0.0" pytest-env = "1.1.5" From a96cc92d77186b6ffd39656a8da88f3ea61a6dba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 11:37:02 -0500 Subject: [PATCH 21/24] chore(deps-dev): bump mkdocs-material from 9.5.50 to 9.6.2 (#6799) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index ce20b23af4..ef99dd9c0d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2536,13 +2536,13 @@ dev = ["click", "codecov", "mkdocs-gen-files", "mkdocs-git-authors-plugin", "mkd [[package]] name = "mkdocs-material" -version = "9.5.50" +version = "9.6.2" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.50-py3-none-any.whl", hash = "sha256:f24100f234741f4d423a9d672a909d859668a4f404796be3cf035f10d6050385"}, - {file = "mkdocs_material-9.5.50.tar.gz", hash = "sha256:ae5fe16f3d7c9ccd05bb6916a7da7420cf99a9ce5e33debd9d40403a090d5825"}, + {file = "mkdocs_material-9.6.2-py3-none-any.whl", hash = "sha256:71d90dbd63b393ad11a4d90151dfe3dcbfcd802c0f29ce80bebd9bbac6abc753"}, + {file = "mkdocs_material-9.6.2.tar.gz", hash = "sha256:a3de1c5d4c745f10afa78b1a02f917b9dce0808fb206adc0f5bb48b58c1ca21f"}, ] [package.dependencies] @@ -5090,4 +5090,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "862dc654b14d6b85f9d8951bbf476b3d9f2fe60b5af1de0e7a69337178244584" +content-hash = "3d3b3f7704292b140d005732ab1ea87cfeeac209889cc941b31eeec486cb268e" diff --git a/pyproject.toml b/pyproject.toml index c881814f81..9a6a04df43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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.50" +mkdocs-material = "9.6.2" mkdocs-material-extensions = "1.3.1" [tool.poetry.scripts] From d64f611f519d7f2237dfb1ebc249e798525bd986 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:48:18 -0500 Subject: [PATCH 22/24] chore(deps): bump pytz from 2024.2 to 2025.1 (#6765) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index ef99dd9c0d..1a3d754e90 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3804,13 +3804,13 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2024.2" +version = "2025.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] [[package]] @@ -5090,4 +5090,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "3d3b3f7704292b140d005732ab1ea87cfeeac209889cc941b31eeec486cb268e" +content-hash = "c387413f38df1dc2286d8e16077fdccd2573943bf7cbdaeca2389be0ee0c339c" diff --git a/pyproject.toml b/pyproject.toml index 9a6a04df43..b6d6091419 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ py-ocsf-models = "0.2.0" pydantic = "1.10.18" python = ">=3.9,<3.13" python-dateutil = "^2.9.0.post0" -pytz = "2024.2" +pytz = "2025.1" schema = "0.7.7" shodan = "1.31.0" slack-sdk = "3.34.0" From 552aa64741d700fae9c96ff58cea67d0db3c2c30 Mon Sep 17 00:00:00 2001 From: Drew Kerrigan Date: Mon, 3 Feb 2025 14:51:03 -0500 Subject: [PATCH 23/24] docs(): add description of changed and new delta values to prowler app tutorial (#6801) --- docs/tutorials/prowler-app.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/tutorials/prowler-app.md b/docs/tutorials/prowler-app.md index 40a40ed096..3184d9e958 100644 --- a/docs/tutorials/prowler-app.md +++ b/docs/tutorials/prowler-app.md @@ -133,3 +133,5 @@ While the scan is running, start exploring the findings in these sections: Issues - **Browse All Findings**: Detailed list of findings detected, where you can filter by severity, service, and more. Findings + +To view all `new` findings that have not been seen prior to this scan, click the `Delta` filter and select `new`. To view all `changed` findings that have had a status change (from `PASS` to `FAIL` for example), click the `Delta` filter and select `changed`. From 5f54377407f05dc2145356c4fc54ca89a5b154f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Mart=C3=ADn?= Date: Mon, 3 Feb 2025 20:59:08 +0100 Subject: [PATCH 24/24] chore(aws_audit_manager_control_tower_guardrails): add checks to reqs (#6699) --- ..._manager_control_tower_guardrails_aws.json | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/prowler/compliance/aws/aws_audit_manager_control_tower_guardrails_aws.json b/prowler/compliance/aws/aws_audit_manager_control_tower_guardrails_aws.json index 34a5856fd7..fa4d71c05b 100644 --- a/prowler/compliance/aws/aws_audit_manager_control_tower_guardrails_aws.json +++ b/prowler/compliance/aws/aws_audit_manager_control_tower_guardrails_aws.json @@ -28,7 +28,9 @@ "Service": "ebs" } ], - "Checks": [] + "Checks": [ + "ec2_ebs_volume_snapshots_exists" + ] }, { "Id": "1.0.3", @@ -42,7 +44,8 @@ } ], "Checks": [ - "ec2_ebs_default_encryption" + "ec2_ebs_default_encryption", + "ec2_ebs_volume_encryption" ] }, { @@ -87,7 +90,9 @@ } ], "Checks": [ - "iam_user_mfa_enabled_console_access" + "iam_user_mfa_enabled_console_access", + "iam_user_hardware_mfa_enabled", + "iam_root_mfa_enabled" ] }, { @@ -102,7 +107,9 @@ } ], "Checks": [ - "iam_user_mfa_enabled_console_access" + "iam_user_mfa_enabled_console_access", + "iam_user_hardware_mfa_enabled", + "iam_root_mfa_enabled" ] }, { @@ -117,7 +124,9 @@ } ], "Checks": [ - "iam_root_mfa_enabled" + "iam_root_mfa_enabled", + "iam_root_hardware_mfa_enabled", + "iam_user_mfa_enabled_console_access" ] }, { @@ -162,7 +171,10 @@ } ], "Checks": [ - "rds_instance_no_public_access" + "rds_instance_no_public_access", + "s3_bucket_public_access", + "s3_bucket_public_list_acl", + "s3_account_level_public_access_blocks" ] }, { @@ -192,7 +204,8 @@ } ], "Checks": [ - "rds_instance_storage_encrypted" + "rds_instance_storage_encrypted", + "rds_instance_transport_encrypted" ] }, {