mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-03-22 03:08:23 +00:00
Compare commits
19 Commits
9ae35029dc
...
backport/v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75a429740e | ||
|
|
e7f5ad1bf2 | ||
|
|
e3d7fe4436 | ||
|
|
3bd5f4f650 | ||
|
|
30dd1df2ae | ||
|
|
d338123c62 | ||
|
|
ff4a38a985 | ||
|
|
db23a1be5f | ||
|
|
0f62ec3e07 | ||
|
|
609137521b | ||
|
|
f141b8e87f | ||
|
|
ad3aa2a2cc | ||
|
|
1e4ac36de9 | ||
|
|
2fa8d7c351 | ||
|
|
39306e47f2 | ||
|
|
084b33c5dd | ||
|
|
06ce25d6a2 | ||
|
|
061cad9bda | ||
|
|
7d4541a1be |
6
.github/workflows/api-pull-request.yml
vendored
6
.github/workflows/api-pull-request.yml
vendored
@@ -136,12 +136,6 @@ jobs:
|
||||
run: |
|
||||
poetry check --lock
|
||||
|
||||
- name: Prevents known compatibility error between lxml and libxml2/libxmlsec versions - https://github.com/xmlsec/python-xmlsec/issues/320
|
||||
working-directory: ./api
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
run: |
|
||||
poetry run pip install --force-reinstall --no-binary lxml lxml
|
||||
|
||||
- name: Lint with ruff
|
||||
working-directory: ./api
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
|
||||
@@ -4,6 +4,42 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
## [v1.10.0] (Prowler UNRELEASED)
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
### Added
|
||||
|
||||
- SSO with SAML support [(#8175)](https://github.com/prowler-cloud/prowler/pull/8175)
|
||||
- `/processors` endpoints to post-process findings. Currently, only the Mutelist processor is supported to allow to mute findings.
|
||||
|
||||
### Security
|
||||
|
||||
- Enhanced password validation to enforce 12+ character passwords with special characters, uppercase, lowercase, and numbers [(#8225)](https://github.com/prowler-cloud/prowler/pull/8225)
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.2] (Prowler v5.8.2)
|
||||
|
||||
### Fixed
|
||||
- Pinned lxml and xmlsec versions to avoid compatibility issues [(#8253)](https://github.com/prowler-cloud/prowler/pull/8253)
|
||||
|
||||
>>>>>>> 8bcec4926 (fix: set lxml version (#8253))
|
||||
---
|
||||
|
||||
## [v1.9.1] (Prowler v5.8.1)
|
||||
|
||||
### Added
|
||||
- Custom exception for provider connection errors during scans [(#8234)](https://github.com/prowler-cloud/prowler/pull/8234)
|
||||
|
||||
### Changed
|
||||
- Summary and overview tasks now use a dedicated queue and no longer propagate errors to compliance tasks [(#8214)](https://github.com/prowler-cloud/prowler/pull/8214)
|
||||
|
||||
### Fixed
|
||||
- Scan with no resources will not trigger legacy code for findings metadata [(#8183)](https://github.com/prowler-cloud/prowler/pull/8183)
|
||||
- Invitation email comparison case-insensitive [(#8206)](https://github.com/prowler-cloud/prowler/pull/8206)
|
||||
|
||||
### Removed
|
||||
- Validation of the provider's secret type during updates [(#8197)](https://github.com/prowler-cloud/prowler/pull/8197)
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.0] (Prowler v5.8.0)
|
||||
|
||||
@@ -57,10 +57,6 @@ RUN poetry install --no-root && \
|
||||
|
||||
RUN poetry run python "$(poetry env info --path)/src/prowler/prowler/providers/m365/lib/powershell/m365_powershell.py"
|
||||
|
||||
# Prevents known compatibility error between lxml and libxml2/libxmlsec versions.
|
||||
# See: https://github.com/xmlsec/python-xmlsec/issues/320
|
||||
RUN poetry run pip install --force-reinstall --no-binary lxml lxml
|
||||
|
||||
COPY src/backend/ ./backend/
|
||||
COPY docker-entrypoint.sh ./docker-entrypoint.sh
|
||||
|
||||
|
||||
@@ -32,7 +32,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,scan-reports,deletion,backfill -E --max-tasks-per-child 1
|
||||
poetry run python -m celery -A config.celery worker -l "${DJANGO_LOGGING_LEVEL:-info}" -Q celery,scans,scan-reports,deletion,backfill,overview -E --max-tasks-per-child 1
|
||||
}
|
||||
|
||||
start_worker_beat() {
|
||||
|
||||
1787
api/poetry.lock
generated
1787
api/poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -23,12 +23,18 @@ dependencies = [
|
||||
"drf-spectacular==0.27.2",
|
||||
"drf-spectacular-jsonapi==0.5.1",
|
||||
"gunicorn==23.0.0",
|
||||
<<<<<<< HEAD
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@v5.8",
|
||||
=======
|
||||
"lxml==5.3.2",
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@master",
|
||||
>>>>>>> 8bcec4926 (fix: set lxml version (#8253))
|
||||
"psycopg2-binary==2.9.9",
|
||||
"pytest-celery[redis] (>=1.0.1,<2.0.0)",
|
||||
"sentry-sdk[django] (>=2.20.0,<3.0.0)",
|
||||
"uuid6==2024.7.10",
|
||||
"openai (>=1.82.0,<2.0.0)"
|
||||
"openai (>=1.82.0,<2.0.0)",
|
||||
"xmlsec==1.3.14"
|
||||
]
|
||||
description = "Prowler's API (Django/DRF)"
|
||||
license = "Apache-2.0"
|
||||
@@ -36,7 +42,7 @@ name = "prowler-api"
|
||||
package-mode = false
|
||||
# Needed for the SDK compatibility
|
||||
requires-python = ">=3.11,<3.13"
|
||||
version = "1.9.0"
|
||||
version = "1.9.1"
|
||||
|
||||
[project.scripts]
|
||||
celery = "src.backend.config.settings.celery"
|
||||
|
||||
@@ -57,6 +57,11 @@ class TaskInProgressException(TaskManagementError):
|
||||
super().__init__()
|
||||
|
||||
|
||||
# Provider connection errors
|
||||
class ProviderConnectionError(Exception):
|
||||
"""Base exception for provider connection errors."""
|
||||
|
||||
|
||||
def custom_exception_handler(exc, context):
|
||||
if isinstance(exc, django_validation_error):
|
||||
if hasattr(exc, "error_dict"):
|
||||
|
||||
@@ -936,6 +936,11 @@ class Invitation(RowLevelSecurityProtectedModel):
|
||||
null=True,
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.email:
|
||||
self.email = self.email.strip().lower()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
class Meta(RowLevelSecurityProtectedModel.Meta):
|
||||
db_table = "invitations"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Prowler API
|
||||
version: 1.9.0
|
||||
version: 1.9.1
|
||||
description: |-
|
||||
Prowler API specification.
|
||||
|
||||
@@ -9661,7 +9661,6 @@ components:
|
||||
* `static` - Key-value pairs
|
||||
* `role` - Role assumption
|
||||
* `service_account` - GCP Service Account Key
|
||||
readOnly: true
|
||||
secret:
|
||||
oneOf:
|
||||
- type: object
|
||||
@@ -11373,7 +11372,6 @@ components:
|
||||
* `static` - Key-value pairs
|
||||
* `role` - Role assumption
|
||||
* `service_account` - GCP Service Account Key
|
||||
readOnly: true
|
||||
secret:
|
||||
oneOf:
|
||||
- type: object
|
||||
|
||||
@@ -254,7 +254,7 @@ class TestValidateInvitation:
|
||||
|
||||
assert result == invitation
|
||||
mock_db.get.assert_called_once_with(
|
||||
token="VALID_TOKEN", email="user@example.com"
|
||||
token="VALID_TOKEN", email__iexact="user@example.com"
|
||||
)
|
||||
|
||||
def test_invitation_not_found_raises_validation_error(self):
|
||||
@@ -269,7 +269,7 @@ class TestValidateInvitation:
|
||||
"invitation_token": "Invalid invitation code."
|
||||
}
|
||||
mock_db.get.assert_called_once_with(
|
||||
token="INVALID_TOKEN", email="user@example.com"
|
||||
token="INVALID_TOKEN", email__iexact="user@example.com"
|
||||
)
|
||||
|
||||
def test_invitation_not_found_raises_not_found(self):
|
||||
@@ -284,7 +284,7 @@ class TestValidateInvitation:
|
||||
|
||||
assert exc_info.value.detail == "Invitation is not valid."
|
||||
mock_db.get.assert_called_once_with(
|
||||
token="INVALID_TOKEN", email="user@example.com"
|
||||
token="INVALID_TOKEN", email__iexact="user@example.com"
|
||||
)
|
||||
|
||||
def test_invitation_expired(self, invitation):
|
||||
@@ -332,5 +332,27 @@ class TestValidateInvitation:
|
||||
"invitation_token": "Invalid invitation code."
|
||||
}
|
||||
mock_db.get.assert_called_once_with(
|
||||
token="VALID_TOKEN", email="different@example.com"
|
||||
token="VALID_TOKEN", email__iexact="different@example.com"
|
||||
)
|
||||
|
||||
def test_valid_invitation_uppercase_email(self):
|
||||
"""Test that validate_invitation works with case-insensitive email lookup."""
|
||||
uppercase_email = "USER@example.com"
|
||||
|
||||
invitation = MagicMock(spec=Invitation)
|
||||
invitation.token = "VALID_TOKEN"
|
||||
invitation.email = uppercase_email
|
||||
invitation.expires_at = datetime.now(timezone.utc) + timedelta(days=1)
|
||||
invitation.state = Invitation.State.PENDING
|
||||
invitation.tenant = MagicMock()
|
||||
|
||||
with patch("api.utils.Invitation.objects.using") as mock_using:
|
||||
mock_db = mock_using.return_value
|
||||
mock_db.get.return_value = invitation
|
||||
|
||||
result = validate_invitation("VALID_TOKEN", "user@example.com")
|
||||
|
||||
assert result == invitation
|
||||
mock_db.get.assert_called_once_with(
|
||||
token="VALID_TOKEN", email__iexact="user@example.com"
|
||||
)
|
||||
|
||||
@@ -2029,6 +2029,104 @@ class TestProviderSecretViewSet:
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
def test_provider_secrets_partial_update_with_secret_type(
|
||||
self, authenticated_client, provider_secret_fixture
|
||||
):
|
||||
provider_secret, *_ = provider_secret_fixture
|
||||
data = {
|
||||
"data": {
|
||||
"type": "provider-secrets",
|
||||
"id": str(provider_secret.id),
|
||||
"attributes": {
|
||||
"name": "new_name",
|
||||
"secret": {
|
||||
"service_account_key": {},
|
||||
},
|
||||
"secret_type": "service_account",
|
||||
},
|
||||
"relationships": {
|
||||
"provider": {
|
||||
"data": {
|
||||
"type": "providers",
|
||||
"id": str(provider_secret.provider.id),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
response = authenticated_client.patch(
|
||||
reverse("providersecret-detail", kwargs={"pk": provider_secret.id}),
|
||||
data=json.dumps(data),
|
||||
content_type="application/vnd.api+json",
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
provider_secret.refresh_from_db()
|
||||
assert provider_secret.name == "new_name"
|
||||
assert provider_secret.secret == {"service_account_key": {}}
|
||||
|
||||
def test_provider_secrets_partial_update_with_invalid_secret_type(
|
||||
self, authenticated_client, provider_secret_fixture
|
||||
):
|
||||
provider_secret, *_ = provider_secret_fixture
|
||||
data = {
|
||||
"data": {
|
||||
"type": "provider-secrets",
|
||||
"id": str(provider_secret.id),
|
||||
"attributes": {
|
||||
"name": "new_name",
|
||||
"secret": {
|
||||
"service_account_key": {},
|
||||
},
|
||||
"secret_type": "static",
|
||||
},
|
||||
"relationships": {
|
||||
"provider": {
|
||||
"data": {
|
||||
"type": "providers",
|
||||
"id": str(provider_secret.provider.id),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
response = authenticated_client.patch(
|
||||
reverse("providersecret-detail", kwargs={"pk": provider_secret.id}),
|
||||
data=json.dumps(data),
|
||||
content_type="application/vnd.api+json",
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
def test_provider_secrets_partial_update_without_secret_type_but_different(
|
||||
self, authenticated_client, provider_secret_fixture
|
||||
):
|
||||
provider_secret, *_ = provider_secret_fixture
|
||||
data = {
|
||||
"data": {
|
||||
"type": "provider-secrets",
|
||||
"id": str(provider_secret.id),
|
||||
"attributes": {
|
||||
"name": "new_name",
|
||||
"secret": {
|
||||
"service_account_key": {},
|
||||
},
|
||||
},
|
||||
"relationships": {
|
||||
"provider": {
|
||||
"data": {
|
||||
"type": "providers",
|
||||
"id": str(provider_secret.provider.id),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
response = authenticated_client.patch(
|
||||
reverse("providersecret-detail", kwargs={"pk": provider_secret.id}),
|
||||
data=json.dumps(data),
|
||||
content_type="application/vnd.api+json",
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestScanViewSet:
|
||||
@@ -3371,6 +3469,61 @@ class TestFindingViewSet:
|
||||
]
|
||||
}
|
||||
|
||||
def test_findings_metadata_backfill(
|
||||
self, authenticated_client, scans_fixture, findings_fixture
|
||||
):
|
||||
scan = scans_fixture[0]
|
||||
scan.unique_resource_count = 1
|
||||
scan.save()
|
||||
|
||||
with patch(
|
||||
"api.v1.views.backfill_scan_resource_summaries_task.apply_async"
|
||||
) as mock_backfill_task:
|
||||
response = authenticated_client.get(
|
||||
reverse("finding-metadata"),
|
||||
{"filter[scan]": str(scan.id)},
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_backfill_task.assert_called()
|
||||
|
||||
def test_findings_metadata_backfill_no_resources(
|
||||
self, authenticated_client, scans_fixture
|
||||
):
|
||||
scan_id = str(scans_fixture[0].id)
|
||||
with patch(
|
||||
"api.v1.views.backfill_scan_resource_summaries_task.apply_async"
|
||||
) as mock_backfill_task:
|
||||
response = authenticated_client.get(
|
||||
reverse("finding-metadata"),
|
||||
{"filter[scan]": scan_id},
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_backfill_task.assert_not_called()
|
||||
|
||||
def test_findings_metadata_latest_backfill(
|
||||
self, authenticated_client, scans_fixture, findings_fixture
|
||||
):
|
||||
scan = scans_fixture[0]
|
||||
scan.unique_resource_count = 1
|
||||
scan.save()
|
||||
|
||||
with patch(
|
||||
"api.v1.views.backfill_scan_resource_summaries_task.apply_async"
|
||||
) as mock_backfill_task:
|
||||
response = authenticated_client.get(reverse("finding-metadata_latest"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_backfill_task.assert_called()
|
||||
|
||||
def test_findings_metadata_latest_backfill_no_resources(
|
||||
self, authenticated_client, scans_fixture
|
||||
):
|
||||
with patch(
|
||||
"api.v1.views.backfill_scan_resource_summaries_task.apply_async"
|
||||
) as mock_backfill_task:
|
||||
response = authenticated_client.get(reverse("finding-metadata_latest"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_backfill_task.assert_not_called()
|
||||
|
||||
def test_findings_latest(self, authenticated_client, latest_scan_finding):
|
||||
response = authenticated_client.get(
|
||||
reverse("finding-latest"),
|
||||
|
||||
@@ -187,7 +187,7 @@ def validate_invitation(
|
||||
# Admin DB connector is used to bypass RLS protection since the invitation belongs to a tenant the user
|
||||
# is not a member of yet
|
||||
invitation = Invitation.objects.using(MainRouter.admin_db).get(
|
||||
token=invitation_token, email=email
|
||||
token=invitation_token, email__iexact=email
|
||||
)
|
||||
except Invitation.DoesNotExist:
|
||||
if raise_not_found:
|
||||
|
||||
@@ -1308,12 +1308,13 @@ class ProviderSecretUpdateSerializer(BaseWriteProviderSecretSerializer):
|
||||
"inserted_at": {"read_only": True},
|
||||
"updated_at": {"read_only": True},
|
||||
"provider": {"read_only": True},
|
||||
"secret_type": {"read_only": True},
|
||||
"secret_type": {"required": False},
|
||||
}
|
||||
|
||||
def validate(self, attrs):
|
||||
provider = self.instance.provider
|
||||
secret_type = self.instance.secret_type
|
||||
# To allow updating a secret with the same type without making the `secret_type` mandatory
|
||||
secret_type = attrs.get("secret_type") or self.instance.secret_type
|
||||
secret = attrs.get("secret")
|
||||
|
||||
validated_attrs = super().validate(attrs)
|
||||
|
||||
@@ -271,7 +271,7 @@ class SchemaView(SpectacularAPIView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
spectacular_settings.TITLE = "Prowler API"
|
||||
spectacular_settings.VERSION = "1.9.0"
|
||||
spectacular_settings.VERSION = "1.9.1"
|
||||
spectacular_settings.DESCRIPTION = (
|
||||
"Prowler API specification.\n\nThis file is auto-generated."
|
||||
)
|
||||
@@ -2127,9 +2127,12 @@ class FindingViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
|
||||
# ToRemove: Temporary fallback mechanism
|
||||
if not queryset.exists():
|
||||
scan_ids = Scan.objects.filter(
|
||||
raw_scans_ids = Scan.objects.filter(
|
||||
tenant_id=tenant_id, **scan_based_filters
|
||||
).values_list("id", flat=True)
|
||||
).values_list("id", "unique_resource_count")
|
||||
scan_ids = [
|
||||
scan_id for scan_id, count in raw_scans_ids if count and count > 0
|
||||
]
|
||||
for scan_id in scan_ids:
|
||||
backfill_scan_resource_summaries_task.apply_async(
|
||||
kwargs={"tenant_id": tenant_id, "scan_id": scan_id}
|
||||
@@ -2215,7 +2218,12 @@ class FindingViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
.order_by("provider_id", "-inserted_at")
|
||||
.distinct("provider_id")
|
||||
)
|
||||
latest_scans_ids = list(latest_scans_queryset.values_list("id", flat=True))
|
||||
raw_latest_scans_ids = list(
|
||||
latest_scans_queryset.values_list("id", "unique_resource_count")
|
||||
)
|
||||
latest_scans_ids = [
|
||||
scan_id for scan_id, count in raw_latest_scans_ids if count and count > 0
|
||||
]
|
||||
|
||||
queryset = ResourceScanSummary.objects.filter(
|
||||
tenant_id=tenant_id,
|
||||
|
||||
@@ -4,6 +4,7 @@ from config.env import env
|
||||
IGNORED_EXCEPTIONS = [
|
||||
# Provider is not connected due to credentials errors
|
||||
"is not connected",
|
||||
"ProviderConnectionError",
|
||||
# Authentication Errors from AWS
|
||||
"InvalidToken",
|
||||
"AccessDeniedException",
|
||||
@@ -16,7 +17,7 @@ IGNORED_EXCEPTIONS = [
|
||||
"InternalServerErrorException",
|
||||
"AccessDenied",
|
||||
"No Shodan API Key", # Shodan Check
|
||||
"RequestLimitExceeded", # For now we don't want to log the RequestLimitExceeded errors
|
||||
"RequestLimitExceeded", # For now, we don't want to log the RequestLimitExceeded errors
|
||||
"ThrottlingException",
|
||||
"Rate exceeded",
|
||||
"SubscriptionRequiredException",
|
||||
@@ -42,7 +43,9 @@ IGNORED_EXCEPTIONS = [
|
||||
"AWSAccessKeyIDInvalidError",
|
||||
"AWSSessionTokenExpiredError",
|
||||
"EndpointConnectionError", # AWS Service is not available in a region
|
||||
"Pool is closed", # The following comes from urllib3: eu-west-1 -- HTTPClientError[126]: An HTTP Client raised an unhandled exception: AWSHTTPSConnectionPool(host='hostname.s3.eu-west-1.amazonaws.com', port=443): Pool is closed.
|
||||
# The following comes from urllib3: eu-west-1 -- HTTPClientError[126]: An HTTP Client raised an
|
||||
# unhandled exception: AWSHTTPSConnectionPool(host='hostname.s3.eu-west-1.amazonaws.com', port=443): Pool is closed.
|
||||
"Pool is closed",
|
||||
# Authentication Errors from GCP
|
||||
"ClientAuthenticationError",
|
||||
"AuthorizationFailed",
|
||||
@@ -71,7 +74,7 @@ IGNORED_EXCEPTIONS = [
|
||||
|
||||
def before_send(event, hint):
|
||||
"""
|
||||
before_send handles the Sentry events in order to sent them or not
|
||||
before_send handles the Sentry events in order to send them or not
|
||||
"""
|
||||
# Ignore logs with the ignored_exceptions
|
||||
# https://docs.python.org/3/library/logging.html#logrecord-objects
|
||||
|
||||
@@ -14,6 +14,7 @@ from api.compliance import (
|
||||
generate_scan_compliance,
|
||||
)
|
||||
from api.db_utils import create_objects_in_batches, rls_transaction
|
||||
from api.exceptions import ProviderConnectionError
|
||||
from api.models import (
|
||||
ComplianceRequirementOverview,
|
||||
Finding,
|
||||
@@ -139,7 +140,7 @@ def perform_prowler_scan(
|
||||
provider_instance.connected = True
|
||||
except Exception as e:
|
||||
provider_instance.connected = False
|
||||
exc = ValueError(
|
||||
exc = ProviderConnectionError(
|
||||
f"Provider {provider_instance.provider} is not connected: {e}"
|
||||
)
|
||||
finally:
|
||||
|
||||
@@ -37,6 +37,26 @@ from prowler.lib.outputs.finding import Finding as FindingOutput
|
||||
logger = get_task_logger(__name__)
|
||||
|
||||
|
||||
def _perform_scan_complete_tasks(tenant_id: str, scan_id: str, provider_id: str):
|
||||
"""
|
||||
Helper function to perform tasks after a scan is completed.
|
||||
|
||||
Args:
|
||||
tenant_id (str): The tenant ID under which the scan was performed.
|
||||
scan_id (str): The ID of the scan that was performed.
|
||||
provider_id (str): The primary key of the Provider instance that was scanned.
|
||||
"""
|
||||
create_compliance_requirements_task.apply_async(
|
||||
kwargs={"tenant_id": tenant_id, "scan_id": scan_id}
|
||||
)
|
||||
chain(
|
||||
perform_scan_summary_task.si(tenant_id=tenant_id, scan_id=scan_id),
|
||||
generate_outputs_task.si(
|
||||
scan_id=scan_id, provider_id=provider_id, tenant_id=tenant_id
|
||||
),
|
||||
).apply_async()
|
||||
|
||||
|
||||
@shared_task(base=RLSTask, name="provider-connection-check")
|
||||
@set_tenant
|
||||
def check_provider_connection_task(provider_id: str):
|
||||
@@ -103,13 +123,7 @@ def perform_scan_task(
|
||||
checks_to_execute=checks_to_execute,
|
||||
)
|
||||
|
||||
chain(
|
||||
perform_scan_summary_task.si(tenant_id, scan_id),
|
||||
create_compliance_requirements_task.si(tenant_id=tenant_id, scan_id=scan_id),
|
||||
generate_outputs.si(
|
||||
scan_id=scan_id, provider_id=provider_id, tenant_id=tenant_id
|
||||
),
|
||||
).apply_async()
|
||||
_perform_scan_complete_tasks(tenant_id, scan_id, provider_id)
|
||||
|
||||
return result
|
||||
|
||||
@@ -214,20 +228,12 @@ def perform_scheduled_scan_task(self, tenant_id: str, provider_id: str):
|
||||
scheduler_task_id=periodic_task_instance.id,
|
||||
)
|
||||
|
||||
chain(
|
||||
perform_scan_summary_task.si(tenant_id, scan_instance.id),
|
||||
create_compliance_requirements_task.si(
|
||||
tenant_id=tenant_id, scan_id=str(scan_instance.id)
|
||||
),
|
||||
generate_outputs.si(
|
||||
scan_id=str(scan_instance.id), provider_id=provider_id, tenant_id=tenant_id
|
||||
),
|
||||
).apply_async()
|
||||
_perform_scan_complete_tasks(tenant_id, str(scan_instance.id), provider_id)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@shared_task(name="scan-summary")
|
||||
@shared_task(name="scan-summary", queue="overview")
|
||||
def perform_scan_summary_task(tenant_id: str, scan_id: str):
|
||||
return aggregate_findings(tenant_id=tenant_id, scan_id=scan_id)
|
||||
|
||||
@@ -243,7 +249,7 @@ def delete_tenant_task(tenant_id: str):
|
||||
queue="scan-reports",
|
||||
)
|
||||
@set_tenant(keep_tenant=True)
|
||||
def generate_outputs(scan_id: str, provider_id: str, tenant_id: str):
|
||||
def generate_outputs_task(scan_id: str, provider_id: str, tenant_id: str):
|
||||
"""
|
||||
Process findings in batches and generate output files in multiple formats.
|
||||
|
||||
@@ -381,7 +387,7 @@ def backfill_scan_resource_summaries_task(tenant_id: str, scan_id: str):
|
||||
return backfill_resource_scan_summaries(tenant_id=tenant_id, scan_id=scan_id)
|
||||
|
||||
|
||||
@shared_task(base=RLSTask, name="scan-compliance-overviews")
|
||||
@shared_task(base=RLSTask, name="scan-compliance-overviews", queue="overview")
|
||||
def create_compliance_requirements_task(tenant_id: str, scan_id: str):
|
||||
"""
|
||||
Creates detailed compliance requirement records for a scan.
|
||||
|
||||
@@ -12,6 +12,7 @@ from tasks.jobs.scan import (
|
||||
)
|
||||
from tasks.utils import CustomEncoder
|
||||
|
||||
from api.exceptions import ProviderConnectionError
|
||||
from api.models import (
|
||||
ComplianceRequirementOverview,
|
||||
Finding,
|
||||
@@ -203,7 +204,7 @@ class TestPerformScan:
|
||||
provider_id = str(provider.id)
|
||||
checks_to_execute = ["check1", "check2"]
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(ProviderConnectionError):
|
||||
perform_prowler_scan(tenant_id, scan_id, provider_id, checks_to_execute)
|
||||
|
||||
scan.refresh_from_db()
|
||||
|
||||
@@ -3,9 +3,10 @@ from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from tasks.tasks import generate_outputs
|
||||
from tasks.tasks import _perform_scan_complete_tasks, generate_outputs_task
|
||||
|
||||
|
||||
# TODO Move this to outputs/reports jobs
|
||||
@pytest.mark.django_db
|
||||
class TestGenerateOutputs:
|
||||
def setup_method(self):
|
||||
@@ -17,7 +18,7 @@ class TestGenerateOutputs:
|
||||
with patch("tasks.tasks.ScanSummary.objects.filter") as mock_filter:
|
||||
mock_filter.return_value.exists.return_value = False
|
||||
|
||||
result = generate_outputs(
|
||||
result = generate_outputs_task(
|
||||
scan_id=self.scan_id,
|
||||
provider_id=self.provider_id,
|
||||
tenant_id=self.tenant_id,
|
||||
@@ -99,7 +100,7 @@ class TestGenerateOutputs:
|
||||
mock_compress.return_value = "/tmp/zipped.zip"
|
||||
mock_upload.return_value = "s3://bucket/zipped.zip"
|
||||
|
||||
result = generate_outputs(
|
||||
result = generate_outputs_task(
|
||||
scan_id=self.scan_id,
|
||||
provider_id=self.provider_id,
|
||||
tenant_id=self.tenant_id,
|
||||
@@ -150,7 +151,7 @@ class TestGenerateOutputs:
|
||||
True,
|
||||
]
|
||||
|
||||
result = generate_outputs(
|
||||
result = generate_outputs_task(
|
||||
scan_id="scan",
|
||||
provider_id="provider",
|
||||
tenant_id=self.tenant_id,
|
||||
@@ -208,7 +209,7 @@ class TestGenerateOutputs:
|
||||
{"aws": [(lambda x: True, MagicMock())]},
|
||||
),
|
||||
):
|
||||
generate_outputs(
|
||||
generate_outputs_task(
|
||||
scan_id=self.scan_id,
|
||||
provider_id=self.provider_id,
|
||||
tenant_id=self.tenant_id,
|
||||
@@ -276,7 +277,7 @@ class TestGenerateOutputs:
|
||||
}
|
||||
},
|
||||
):
|
||||
result = generate_outputs(
|
||||
result = generate_outputs_task(
|
||||
scan_id=self.scan_id,
|
||||
provider_id=self.provider_id,
|
||||
tenant_id=self.tenant_id,
|
||||
@@ -346,7 +347,7 @@ class TestGenerateOutputs:
|
||||
):
|
||||
mock_summary.return_value.exists.return_value = True
|
||||
|
||||
result = generate_outputs(
|
||||
result = generate_outputs_task(
|
||||
scan_id=self.scan_id,
|
||||
provider_id=self.provider_id,
|
||||
tenant_id=self.tenant_id,
|
||||
@@ -407,9 +408,31 @@ class TestGenerateOutputs:
|
||||
),
|
||||
):
|
||||
with caplog.at_level("ERROR"):
|
||||
generate_outputs(
|
||||
generate_outputs_task(
|
||||
scan_id=self.scan_id,
|
||||
provider_id=self.provider_id,
|
||||
tenant_id=self.tenant_id,
|
||||
)
|
||||
assert "Error deleting output files" in caplog.text
|
||||
|
||||
|
||||
class TestScanCompleteTasks:
|
||||
@patch("tasks.tasks.create_compliance_requirements_task.apply_async")
|
||||
@patch("tasks.tasks.perform_scan_summary_task.si")
|
||||
@patch("tasks.tasks.generate_outputs_task.si")
|
||||
def test_scan_complete_tasks(
|
||||
self, mock_outputs_task, mock_scan_summary_task, mock_compliance_tasks
|
||||
):
|
||||
_perform_scan_complete_tasks("tenant-id", "scan-id", "provider-id")
|
||||
mock_compliance_tasks.assert_called_once_with(
|
||||
kwargs={"tenant_id": "tenant-id", "scan_id": "scan-id"},
|
||||
)
|
||||
mock_scan_summary_task.assert_called_once_with(
|
||||
scan_id="scan-id",
|
||||
tenant_id="tenant-id",
|
||||
)
|
||||
mock_outputs_task.assert_called_once_with(
|
||||
scan_id="scan-id",
|
||||
provider_id="provider-id",
|
||||
tenant_id="tenant-id",
|
||||
)
|
||||
|
||||
@@ -37,7 +37,7 @@ prowler github --github-app-id app_id --github-app-key app_key
|
||||
If no login method is explicitly provided, Prowler will automatically attempt to authenticate using environment variables in the following order of precedence:
|
||||
|
||||
1. `GITHUB_PERSONAL_ACCESS_TOKEN`
|
||||
2. `OAUTH_APP_TOKEN`
|
||||
2. `GITHUB_OAUTH_APP_TOKEN`
|
||||
3. `GITHUB_APP_ID` and `GITHUB_APP_KEY`
|
||||
|
||||
???+ note
|
||||
|
||||
@@ -106,6 +106,8 @@ nav:
|
||||
- Getting Started: tutorials/microsoft365/getting-started-m365.md
|
||||
- Authentication: tutorials/microsoft365/authentication.md
|
||||
- Use of PowerShell: tutorials/microsoft365/use-of-powershell.md
|
||||
- GitHub:
|
||||
- Authentication: tutorials/github/authentication.md
|
||||
- IaC:
|
||||
- Getting Started: tutorials/iac/getting-started-iac.md
|
||||
- Developer Guide:
|
||||
|
||||
4
poetry.lock
generated
4
poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "about-time"
|
||||
@@ -2678,6 +2678,8 @@ python-versions = "*"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"},
|
||||
{file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"},
|
||||
{file = "jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
||||
@@ -9,6 +9,17 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
- Add GitHub provider to lateral panel in documentation and change -h environment variable output [(#8246)](https://github.com/prowler-cloud/prowler/pull/8246)
|
||||
|
||||
---
|
||||
|
||||
## [v5.8.1] (Prowler 5.8.1)
|
||||
|
||||
### Fixed
|
||||
- Detect wildcarded ARNs in sts:AssumeRole policy resources [(#8164)](https://github.com/prowler-cloud/prowler/pull/8164)
|
||||
- List all streams and `firehose_stream_encrypted_at_rest` logic [(#8213)](https://github.com/prowler-cloud/prowler/pull/8213)
|
||||
- Allow empty values for http_endpoint in templates [(#8184)](https://github.com/prowler-cloud/prowler/pull/8184)
|
||||
- Convert all Azure Storage models to Pydantic models to avoid serialization issues [(#8222)](https://github.com/prowler-cloud/prowler/pull/8222)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ from prowler.lib.logger import logger
|
||||
|
||||
timestamp = datetime.today()
|
||||
timestamp_utc = datetime.now(timezone.utc).replace(tzinfo=timezone.utc)
|
||||
prowler_version = "5.8.0"
|
||||
prowler_version = "5.8.2"
|
||||
html_logo_url = "https://github.com/prowler-cloud/prowler/"
|
||||
square_logo_img = "https://prowler.com/wp-content/uploads/logo-html.png"
|
||||
aws_logo = "https://user-images.githubusercontent.com/38561120/235953920-3e3fba08-0795-41dc-b480-9bea57db9f2e.png"
|
||||
|
||||
@@ -18,7 +18,10 @@ class ec2_launch_template_imdsv2_required(Check):
|
||||
and version.template_data.http_tokens == "required"
|
||||
):
|
||||
versions_with_imdsv2_required.append(str(version.version_number))
|
||||
elif version.template_data.http_endpoint == "disabled":
|
||||
elif (
|
||||
version.template_data.http_endpoint == "disabled"
|
||||
or not version.template_data.http_endpoint
|
||||
):
|
||||
versions_with_metadata_disabled.append(str(version.version_number))
|
||||
else:
|
||||
versions_with_no_imdsv2.append(str(version.version_number))
|
||||
|
||||
@@ -25,18 +25,47 @@ class Firehose(AWSService):
|
||||
def _list_delivery_streams(self, regional_client):
|
||||
logger.info("Firehose - Listing delivery streams...")
|
||||
try:
|
||||
for stream_name in regional_client.list_delivery_streams()[
|
||||
"DeliveryStreamNames"
|
||||
]:
|
||||
stream_arn = f"arn:{self.audited_partition}:firehose:{regional_client.region}:{self.audited_account}:deliverystream/{stream_name}"
|
||||
if not self.audit_resources or (
|
||||
is_resource_filtered(stream_arn, self.audit_resources)
|
||||
):
|
||||
self.delivery_streams[stream_arn] = DeliveryStream(
|
||||
arn=stream_arn,
|
||||
name=stream_name,
|
||||
region=regional_client.region,
|
||||
# Manual pagination using ExclusiveStartDeliveryStreamName
|
||||
# This ensures we get all streams alphabetically without duplicates
|
||||
exclusive_start_delivery_stream_name = None
|
||||
processed_streams = set()
|
||||
|
||||
while True:
|
||||
kwargs = {}
|
||||
if exclusive_start_delivery_stream_name:
|
||||
kwargs["ExclusiveStartDeliveryStreamName"] = (
|
||||
exclusive_start_delivery_stream_name
|
||||
)
|
||||
|
||||
response = regional_client.list_delivery_streams(**kwargs)
|
||||
stream_names = response.get("DeliveryStreamNames", [])
|
||||
|
||||
for stream_name in stream_names:
|
||||
if stream_name in processed_streams:
|
||||
continue
|
||||
|
||||
processed_streams.add(stream_name)
|
||||
stream_arn = f"arn:{self.audited_partition}:firehose:{regional_client.region}:{self.audited_account}:deliverystream/{stream_name}"
|
||||
|
||||
if not self.audit_resources or (
|
||||
is_resource_filtered(stream_arn, self.audit_resources)
|
||||
):
|
||||
self.delivery_streams[stream_arn] = DeliveryStream(
|
||||
arn=stream_arn,
|
||||
name=stream_name,
|
||||
region=regional_client.region,
|
||||
)
|
||||
|
||||
if not response.get("HasMoreDeliveryStreams", False):
|
||||
break
|
||||
|
||||
# Set the starting point for the next page (last stream name from current batch)
|
||||
# ExclusiveStartDeliveryStreamName will start after this stream alphabetically
|
||||
if stream_names:
|
||||
exclusive_start_delivery_stream_name = stream_names[-1]
|
||||
else:
|
||||
break
|
||||
|
||||
except ClientError as error:
|
||||
logger.error(
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -61,13 +90,45 @@ class Firehose(AWSService):
|
||||
describe_stream = self.regional_clients[
|
||||
stream.region
|
||||
].describe_delivery_stream(DeliveryStreamName=stream.name)
|
||||
|
||||
encryption_config = describe_stream.get(
|
||||
"DeliveryStreamDescription", {}
|
||||
).get("DeliveryStreamEncryptionConfiguration", {})
|
||||
|
||||
stream.kms_encryption = EncryptionStatus(
|
||||
encryption_config.get("Status", "DISABLED")
|
||||
)
|
||||
stream.kms_key_arn = encryption_config.get("KeyARN", "")
|
||||
|
||||
stream.delivery_stream_type = describe_stream.get(
|
||||
"DeliveryStreamDescription", {}
|
||||
).get("DeliveryStreamType", "")
|
||||
|
||||
source_config = describe_stream.get("DeliveryStreamDescription", {}).get(
|
||||
"Source", {}
|
||||
)
|
||||
stream.source = Source(
|
||||
direct_put=DirectPutSourceDescription(
|
||||
troughput_hint_in_mb_per_sec=source_config.get(
|
||||
"DirectPutSourceDescription", {}
|
||||
).get("TroughputHintInMBPerSec", 0)
|
||||
),
|
||||
kinesis_stream=KinesisStreamSourceDescription(
|
||||
kinesis_stream_arn=source_config.get(
|
||||
"KinesisStreamSourceDescription", {}
|
||||
).get("KinesisStreamARN", "")
|
||||
),
|
||||
msk=MSKSourceDescription(
|
||||
msk_cluster_arn=source_config.get("MSKSourceDescription", {}).get(
|
||||
"MSKClusterARN", ""
|
||||
)
|
||||
),
|
||||
database=DatabaseSourceDescription(
|
||||
endpoint=source_config.get("DatabaseSourceDescription", {}).get(
|
||||
"Endpoint", ""
|
||||
)
|
||||
),
|
||||
)
|
||||
except ClientError as error:
|
||||
logger.error(
|
||||
f"{stream.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
@@ -85,6 +146,39 @@ class EncryptionStatus(Enum):
|
||||
DISABLING_FAILED = "DISABLING_FAILED"
|
||||
|
||||
|
||||
class DirectPutSourceDescription(BaseModel):
|
||||
"""Model for the DirectPut source of a Firehose stream"""
|
||||
|
||||
troughput_hint_in_mb_per_sec: int = Field(default_factory=int)
|
||||
|
||||
|
||||
class KinesisStreamSourceDescription(BaseModel):
|
||||
"""Model for the KinesisStream source of a Firehose stream"""
|
||||
|
||||
kinesis_stream_arn: str = Field(default_factory=str)
|
||||
|
||||
|
||||
class MSKSourceDescription(BaseModel):
|
||||
"""Model for the MSK source of a Firehose stream"""
|
||||
|
||||
msk_cluster_arn: str = Field(default_factory=str)
|
||||
|
||||
|
||||
class DatabaseSourceDescription(BaseModel):
|
||||
"""Model for the Database source of a Firehose stream"""
|
||||
|
||||
endpoint: str = Field(default_factory=str)
|
||||
|
||||
|
||||
class Source(BaseModel):
|
||||
"""Model for the source of a Firehose stream"""
|
||||
|
||||
direct_put: Optional[DirectPutSourceDescription]
|
||||
kinesis_stream: Optional[KinesisStreamSourceDescription]
|
||||
msk: Optional[MSKSourceDescription]
|
||||
database: Optional[DatabaseSourceDescription]
|
||||
|
||||
|
||||
class DeliveryStream(BaseModel):
|
||||
"""Model for a Firehose Delivery Stream"""
|
||||
|
||||
@@ -94,3 +188,5 @@ class DeliveryStream(BaseModel):
|
||||
kms_key_arn: Optional[str] = Field(default_factory=str)
|
||||
kms_encryption: Optional[str] = Field(default_factory=str)
|
||||
tags: Optional[List[Dict[str, str]]] = Field(default_factory=list)
|
||||
delivery_stream_type: Optional[str] = Field(default_factory=str)
|
||||
source: Source = Field(default_factory=Source)
|
||||
|
||||
@@ -3,6 +3,8 @@ from typing import List
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.firehose.firehose_client import firehose_client
|
||||
from prowler.providers.aws.services.firehose.firehose_service import EncryptionStatus
|
||||
from prowler.providers.aws.services.kinesis.kinesis_client import kinesis_client
|
||||
from prowler.providers.aws.services.kinesis.kinesis_service import EncryptionType
|
||||
|
||||
|
||||
class firehose_stream_encrypted_at_rest(Check):
|
||||
@@ -22,14 +24,22 @@ class firehose_stream_encrypted_at_rest(Check):
|
||||
findings = []
|
||||
for stream in firehose_client.delivery_streams.values():
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=stream)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Firehose Stream {stream.name} does have at rest encryption enabled."
|
||||
)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled or the source stream is not encrypted."
|
||||
|
||||
if stream.kms_encryption != EncryptionStatus.ENABLED:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled."
|
||||
# Encrypted Kinesis Stream source
|
||||
if stream.delivery_stream_type == "KinesisStreamAsSource":
|
||||
source_stream = kinesis_client.streams.get(
|
||||
stream.source.kinesis_stream.kinesis_stream_arn
|
||||
)
|
||||
if source_stream.encrypted_at_rest != EncryptionType.NONE:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled but the source stream {source_stream.name} has at rest encryption enabled."
|
||||
|
||||
# Check if the stream has encryption enabled directly
|
||||
elif stream.kms_encryption == EncryptionStatus.ENABLED:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Firehose Stream {stream.name} does have at rest encryption enabled."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
|
||||
@@ -5,6 +5,14 @@ from prowler.providers.aws.services.iam.iam_client import iam_client
|
||||
class iam_no_custom_policy_permissive_role_assumption(Check):
|
||||
def execute(self) -> Check_Report_AWS:
|
||||
findings = []
|
||||
|
||||
def resource_has_wildcard(resource):
|
||||
if isinstance(resource, str):
|
||||
return "*" in resource
|
||||
if isinstance(resource, list):
|
||||
return any("*" in r for r in resource)
|
||||
return False
|
||||
|
||||
for policy in iam_client.policies:
|
||||
# Check only custom policies
|
||||
if policy.type == "Custom":
|
||||
@@ -12,6 +20,7 @@ class iam_no_custom_policy_permissive_role_assumption(Check):
|
||||
report.region = iam_client.region
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Custom Policy {policy.name} does not allow permissive STS Role assumption."
|
||||
|
||||
if policy.document:
|
||||
if not isinstance(policy.document["Statement"], list):
|
||||
policy_statements = [policy.document["Statement"]]
|
||||
@@ -19,30 +28,23 @@ class iam_no_custom_policy_permissive_role_assumption(Check):
|
||||
policy_statements = policy.document["Statement"]
|
||||
for statement in policy_statements:
|
||||
if (
|
||||
statement["Effect"] == "Allow"
|
||||
statement.get("Effect") == "Allow"
|
||||
and "Action" in statement
|
||||
and "Resource" in statement
|
||||
and "*" in statement["Resource"]
|
||||
and resource_has_wildcard(statement["Resource"])
|
||||
):
|
||||
if isinstance(statement["Action"], list):
|
||||
for action in statement["Action"]:
|
||||
if (
|
||||
action == "sts:AssumeRole"
|
||||
or action == "sts:*"
|
||||
or action == "*"
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Custom Policy {policy.name} allows permissive STS Role assumption."
|
||||
break
|
||||
else:
|
||||
if (
|
||||
statement["Action"] == "sts:AssumeRole"
|
||||
or statement["Action"] == "sts:*"
|
||||
or statement["Action"] == "*"
|
||||
):
|
||||
actions = (
|
||||
statement["Action"]
|
||||
if isinstance(statement["Action"], list)
|
||||
else [statement["Action"]]
|
||||
)
|
||||
for action in actions:
|
||||
if action in ["sts:AssumeRole", "sts:*", "*"]:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Custom Policy {policy.name} allows permissive STS Role assumption."
|
||||
break
|
||||
break
|
||||
if report.status == "FAIL":
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import List, Optional
|
||||
from typing import Optional
|
||||
|
||||
from azure.mgmt.storage import StorageManagementClient
|
||||
from pydantic import BaseModel
|
||||
@@ -33,7 +32,7 @@ class Storage(AzureService):
|
||||
resouce_group_name = None
|
||||
key_expiration_period_in_days = None
|
||||
if storage_account.key_policy:
|
||||
key_expiration_period_in_days = (
|
||||
key_expiration_period_in_days = int(
|
||||
storage_account.key_policy.key_expiration_period_in_days
|
||||
)
|
||||
replication_settings = ReplicationSettings(storage_account.sku.name)
|
||||
@@ -181,30 +180,26 @@ class Storage(AzureService):
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DeleteRetentionPolicy:
|
||||
class DeleteRetentionPolicy(BaseModel):
|
||||
enabled: bool
|
||||
days: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class BlobProperties:
|
||||
class BlobProperties(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
type: str
|
||||
default_service_version: str
|
||||
container_delete_retention_policy: DeleteRetentionPolicy
|
||||
versioning_enabled: bool = False
|
||||
default_service_version: Optional[str] = None
|
||||
versioning_enabled: Optional[bool] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class NetworkRuleSet:
|
||||
class NetworkRuleSet(BaseModel):
|
||||
bypass: str
|
||||
default_action: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class PrivateEndpointConnection:
|
||||
class PrivateEndpointConnection(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
type: str
|
||||
@@ -228,20 +223,19 @@ class FileServiceProperties(BaseModel):
|
||||
share_delete_retention_policy: DeleteRetentionPolicy
|
||||
|
||||
|
||||
@dataclass
|
||||
class Account:
|
||||
class Account(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
location: str
|
||||
resouce_group_name: str
|
||||
enable_https_traffic_only: bool
|
||||
infrastructure_encryption: bool
|
||||
infrastructure_encryption: Optional[bool] = None
|
||||
allow_blob_public_access: bool
|
||||
network_rule_set: NetworkRuleSet
|
||||
encryption_type: str
|
||||
minimum_tls_version: str
|
||||
private_endpoint_connections: List[PrivateEndpointConnection]
|
||||
key_expiration_period_in_days: str
|
||||
location: str
|
||||
private_endpoint_connections: list[PrivateEndpointConnection]
|
||||
key_expiration_period_in_days: Optional[int] = None
|
||||
replication_settings: ReplicationSettings = ReplicationSettings.STANDARD_LRS
|
||||
allow_cross_tenant_replication: bool = True
|
||||
allow_shared_key_access: bool = True
|
||||
|
||||
@@ -10,6 +10,7 @@ def init_parser(self):
|
||||
nargs="?",
|
||||
help="Personal Access Token to log in against GitHub",
|
||||
default=None,
|
||||
metavar="GITHUB_PERSONAL_ACCESS_TOKEN",
|
||||
)
|
||||
|
||||
github_auth_subparser.add_argument(
|
||||
@@ -17,6 +18,7 @@ def init_parser(self):
|
||||
nargs="?",
|
||||
help="OAuth App Token to log in against GitHub",
|
||||
default=None,
|
||||
metavar="GITHUB_OAUTH_APP_TOKEN",
|
||||
)
|
||||
|
||||
# GitHub App Authentication
|
||||
@@ -25,10 +27,12 @@ def init_parser(self):
|
||||
nargs="?",
|
||||
help="GitHub App ID to log in against GitHub",
|
||||
default=None,
|
||||
metavar="GITHUB_APP_ID",
|
||||
)
|
||||
github_auth_subparser.add_argument(
|
||||
"--github-app-key",
|
||||
nargs="?",
|
||||
help="GitHub App Key Path to log in against GitHub",
|
||||
default=None,
|
||||
metavar="GITHUB_APP_KEY",
|
||||
)
|
||||
|
||||
@@ -68,7 +68,7 @@ maintainers = [{name = "Prowler Engineering", email = "engineering@prowler.com"}
|
||||
name = "prowler"
|
||||
readme = "README.md"
|
||||
requires-python = ">3.9.1,<3.13"
|
||||
version = "5.8.0"
|
||||
version = "5.8.2"
|
||||
|
||||
[project.scripts]
|
||||
prowler = "prowler.__main__:prowler"
|
||||
|
||||
@@ -27,6 +27,24 @@ def mock_make_api_call(self, operation_name, kwarg):
|
||||
return make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
def mock_make_api_call_empty(self, operation_name, kwarg):
|
||||
if operation_name == "DescribeLaunchTemplateVersions":
|
||||
return {
|
||||
"LaunchTemplateVersions": [
|
||||
{
|
||||
"VersionNumber": 1,
|
||||
"LaunchTemplateData": {
|
||||
"MetadataOptions": {
|
||||
"HttpEndpoint": "",
|
||||
"HttpTokens": "required",
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
return make_api_call(self, operation_name, kwarg)
|
||||
|
||||
|
||||
def mock_make_api_call_not_required(self, operation_name, kwarg):
|
||||
if operation_name == "DescribeLaunchTemplateVersions":
|
||||
return {
|
||||
@@ -134,6 +152,66 @@ class Test_ec2_launch_template_imdsv2_required:
|
||||
)
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_launch_template_imdsv2_required_empty(self):
|
||||
with mock.patch(
|
||||
"botocore.client.BaseClient._make_api_call",
|
||||
new=mock_make_api_call_empty,
|
||||
):
|
||||
ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1)
|
||||
launch_template_name = "test-imdsv2-required-empty"
|
||||
ec2_client.create_launch_template(
|
||||
LaunchTemplateName=launch_template_name,
|
||||
VersionDescription="Launch Template with IMDSv2 required",
|
||||
LaunchTemplateData={
|
||||
"InstanceType": "t1.micro",
|
||||
"MetadataOptions": {
|
||||
"HttpEndpoint": "",
|
||||
"HttpTokens": "required",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
launch_template_id = ec2_client.describe_launch_templates(
|
||||
LaunchTemplateNames=[launch_template_name]
|
||||
)["LaunchTemplates"][0]["LaunchTemplateId"]
|
||||
|
||||
from prowler.providers.aws.services.ec2.ec2_service import EC2
|
||||
|
||||
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,
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.aws.services.ec2.ec2_launch_template_imdsv2_required.ec2_launch_template_imdsv2_required.ec2_client",
|
||||
new=EC2(aws_provider),
|
||||
),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.ec2.ec2_launch_template_imdsv2_required.ec2_launch_template_imdsv2_required import (
|
||||
ec2_launch_template_imdsv2_required,
|
||||
)
|
||||
|
||||
check = ec2_launch_template_imdsv2_required()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"EC2 Launch Template {launch_template_name} has metadata service disabled in the following versions: 1."
|
||||
)
|
||||
assert result[0].resource_id == launch_template_id
|
||||
assert result[0].region == AWS_REGION_US_EAST_1
|
||||
assert (
|
||||
result[0].resource_arn
|
||||
== f"arn:aws:ec2:{AWS_REGION_US_EAST_1}:123456789012:launch-template/{launch_template_id}"
|
||||
)
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
@mock_aws
|
||||
def test_launch_template_imdsv2_not_required(self):
|
||||
with mock.patch(
|
||||
|
||||
@@ -2,8 +2,13 @@ from boto3 import client
|
||||
from moto import mock_aws
|
||||
|
||||
from prowler.providers.aws.services.firehose.firehose_service import (
|
||||
DatabaseSourceDescription,
|
||||
DirectPutSourceDescription,
|
||||
EncryptionStatus,
|
||||
Firehose,
|
||||
KinesisStreamSourceDescription,
|
||||
MSKSourceDescription,
|
||||
Source,
|
||||
)
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_ACCOUNT_NUMBER,
|
||||
@@ -152,3 +157,102 @@ class Test_Firehose_Service:
|
||||
firehose.delivery_streams[arn].kms_key_arn
|
||||
== f"arn:aws:kms:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:key/test-kms-key-id"
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_describe_delivery_stream_source_direct_put(self):
|
||||
# Generate S3 client
|
||||
s3_client = client("s3", region_name=AWS_REGION_EU_WEST_1)
|
||||
s3_client.create_bucket(
|
||||
Bucket="test-bucket",
|
||||
CreateBucketConfiguration={"LocationConstraint": AWS_REGION_EU_WEST_1},
|
||||
)
|
||||
|
||||
# Generate Firehose client
|
||||
firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1)
|
||||
delivery_stream = firehose_client.create_delivery_stream(
|
||||
DeliveryStreamName="test-delivery-stream",
|
||||
DeliveryStreamType="DirectPut",
|
||||
S3DestinationConfiguration={
|
||||
"RoleARN": "arn:aws:iam::012345678901:role/firehose-role",
|
||||
"BucketARN": "arn:aws:s3:::test-bucket",
|
||||
"Prefix": "",
|
||||
"BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5},
|
||||
"CompressionFormat": "UNCOMPRESSED",
|
||||
},
|
||||
Tags=[{"Key": "key", "Value": "value"}],
|
||||
)
|
||||
arn = delivery_stream["DeliveryStreamARN"]
|
||||
|
||||
# Firehose Client for this test class
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
|
||||
firehose = Firehose(aws_provider)
|
||||
|
||||
assert len(firehose.delivery_streams) == 1
|
||||
assert firehose.delivery_streams[arn].delivery_stream_type == "DirectPut"
|
||||
|
||||
# Test Source structure
|
||||
assert isinstance(firehose.delivery_streams[arn].source, Source)
|
||||
assert isinstance(
|
||||
firehose.delivery_streams[arn].source.direct_put, DirectPutSourceDescription
|
||||
)
|
||||
assert isinstance(
|
||||
firehose.delivery_streams[arn].source.kinesis_stream,
|
||||
KinesisStreamSourceDescription,
|
||||
)
|
||||
assert isinstance(
|
||||
firehose.delivery_streams[arn].source.msk, MSKSourceDescription
|
||||
)
|
||||
assert isinstance(
|
||||
firehose.delivery_streams[arn].source.database, DatabaseSourceDescription
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_describe_delivery_stream_source_kinesis_stream(self):
|
||||
# Generate Kinesis client
|
||||
kinesis_client = client("kinesis", region_name=AWS_REGION_EU_WEST_1)
|
||||
kinesis_client.create_stream(
|
||||
StreamName="test-kinesis-stream",
|
||||
ShardCount=1,
|
||||
)
|
||||
kinesis_stream_arn = f"arn:aws:kinesis:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:stream/test-kinesis-stream"
|
||||
|
||||
# Generate Firehose client
|
||||
firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1)
|
||||
delivery_stream = firehose_client.create_delivery_stream(
|
||||
DeliveryStreamName="test-delivery-stream",
|
||||
DeliveryStreamType="KinesisStreamAsSource",
|
||||
KinesisStreamSourceConfiguration={
|
||||
"KinesisStreamARN": kinesis_stream_arn,
|
||||
"RoleARN": "arn:aws:iam::012345678901:role/firehose-role",
|
||||
},
|
||||
S3DestinationConfiguration={
|
||||
"RoleARN": "arn:aws:iam::012345678901:role/firehose-role",
|
||||
"BucketARN": "arn:aws:s3:::test-bucket",
|
||||
"Prefix": "",
|
||||
"BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5},
|
||||
"CompressionFormat": "UNCOMPRESSED",
|
||||
},
|
||||
Tags=[{"Key": "key", "Value": "value"}],
|
||||
)
|
||||
arn = delivery_stream["DeliveryStreamARN"]
|
||||
|
||||
# Firehose Client for this test class
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
|
||||
firehose = Firehose(aws_provider)
|
||||
|
||||
assert len(firehose.delivery_streams) == 1
|
||||
assert (
|
||||
firehose.delivery_streams[arn].delivery_stream_type
|
||||
== "KinesisStreamAsSource"
|
||||
)
|
||||
|
||||
# Test Source structure
|
||||
assert isinstance(firehose.delivery_streams[arn].source, Source)
|
||||
assert isinstance(
|
||||
firehose.delivery_streams[arn].source.kinesis_stream,
|
||||
KinesisStreamSourceDescription,
|
||||
)
|
||||
assert (
|
||||
firehose.delivery_streams[arn].source.kinesis_stream.kinesis_stream_arn
|
||||
== kinesis_stream_arn
|
||||
)
|
||||
|
||||
@@ -198,7 +198,7 @@ class Test_firehose_stream_encrypted_at_rest:
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Firehose Stream {stream_name} does not have at rest encryption enabled."
|
||||
== f"Firehose Stream {stream_name} does not have at rest encryption enabled or the source stream is not encrypted."
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
@@ -253,5 +253,74 @@ class Test_firehose_stream_encrypted_at_rest:
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Firehose Stream {stream_name} does not have at rest encryption enabled."
|
||||
== f"Firehose Stream {stream_name} does not have at rest encryption enabled or the source stream is not encrypted."
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_stream_kinesis_source_encrypted(self):
|
||||
# Generate Kinesis client
|
||||
kinesis_client = client("kinesis", region_name=AWS_REGION_EU_WEST_1)
|
||||
kinesis_client.create_stream(
|
||||
StreamName="test-kinesis-stream",
|
||||
ShardCount=1,
|
||||
)
|
||||
kinesis_stream_arn = f"arn:aws:kinesis:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:stream/test-kinesis-stream"
|
||||
|
||||
# Enable encryption on the Kinesis stream
|
||||
kinesis_client.start_stream_encryption(
|
||||
StreamName="test-kinesis-stream",
|
||||
EncryptionType="KMS",
|
||||
KeyId=f"arn:aws:kms:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:key/test-kms-key-id",
|
||||
)
|
||||
|
||||
# Generate Firehose client
|
||||
firehose_client = client("firehose", region_name=AWS_REGION_EU_WEST_1)
|
||||
delivery_stream = firehose_client.create_delivery_stream(
|
||||
DeliveryStreamName="test-delivery-stream",
|
||||
DeliveryStreamType="KinesisStreamAsSource",
|
||||
KinesisStreamSourceConfiguration={
|
||||
"KinesisStreamARN": kinesis_stream_arn,
|
||||
"RoleARN": "arn:aws:iam::012345678901:role/firehose-role",
|
||||
},
|
||||
S3DestinationConfiguration={
|
||||
"RoleARN": "arn:aws:iam::012345678901:role/firehose-role",
|
||||
"BucketARN": "arn:aws:s3:::test-bucket",
|
||||
"Prefix": "",
|
||||
"BufferingHints": {"IntervalInSeconds": 300, "SizeInMBs": 5},
|
||||
"CompressionFormat": "UNCOMPRESSED",
|
||||
},
|
||||
Tags=[{"Key": "key", "Value": "value"}],
|
||||
)
|
||||
arn = delivery_stream["DeliveryStreamARN"]
|
||||
stream_name = arn.split("/")[-1]
|
||||
|
||||
from prowler.providers.aws.services.firehose.firehose_service import Firehose
|
||||
from prowler.providers.aws.services.kinesis.kinesis_service import Kinesis
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest.firehose_client",
|
||||
new=Firehose(aws_provider),
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest.kinesis_client",
|
||||
new=Kinesis(aws_provider),
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.firehose.firehose_stream_encrypted_at_rest.firehose_stream_encrypted_at_rest import (
|
||||
firehose_stream_encrypted_at_rest,
|
||||
)
|
||||
|
||||
check = firehose_stream_encrypted_at_rest()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Firehose Stream {stream_name} does not have at rest encryption enabled but the source stream test-kinesis-stream has at rest encryption enabled."
|
||||
)
|
||||
|
||||
@@ -232,3 +232,179 @@ class Test_iam_no_custom_policy_permissive_role_assumption:
|
||||
result[1].status_extended,
|
||||
)
|
||||
assert result[1].resource_id == policy_name_permissive
|
||||
|
||||
@mock_aws
|
||||
def test_policy_resource_with_embedded_wildcard_in_arn(self):
|
||||
iam_client = client("iam")
|
||||
policy_name = "policy_with_wildcard_in_arn"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": "arn:aws:iam::*:role/eks-terraform-*",
|
||||
}
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
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.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption import (
|
||||
iam_no_custom_policy_permissive_role_assumption,
|
||||
)
|
||||
|
||||
check = iam_no_custom_policy_permissive_role_assumption()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].resource_arn == arn
|
||||
assert search(
|
||||
"allows permissive STS Role assumption", result[0].status_extended
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
|
||||
@mock_aws
|
||||
def test_policy_resource_list_containing_wildcard(self):
|
||||
iam_client = client("iam")
|
||||
policy_name = "policy_with_resource_list"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": ["arn:aws:iam::123456789012:role/SomeRole", "*"],
|
||||
}
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
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.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption import (
|
||||
iam_no_custom_policy_permissive_role_assumption,
|
||||
)
|
||||
|
||||
check = iam_no_custom_policy_permissive_role_assumption()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].resource_arn == arn
|
||||
assert search(
|
||||
"allows permissive STS Role assumption", result[0].status_extended
|
||||
)
|
||||
assert result[0].resource_id == policy_name
|
||||
|
||||
@mock_aws
|
||||
def test_policy_resource_list_with_arn_wildcards_should_fail(self):
|
||||
iam_client = client("iam")
|
||||
policy_name = "policy_list_wildcard_arn"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": [
|
||||
"arn:aws:iam::123456789012:role/eks-admin",
|
||||
"arn:aws:iam::*:role/eks-*",
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
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.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption import (
|
||||
iam_no_custom_policy_permissive_role_assumption,
|
||||
)
|
||||
|
||||
check = iam_no_custom_policy_permissive_role_assumption()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].resource_arn == arn
|
||||
assert search(
|
||||
"allows permissive STS Role assumption", result[0].status_extended
|
||||
)
|
||||
|
||||
@mock_aws
|
||||
def test_policy_resource_list_with_only_wildcarded_arns_should_fail(self):
|
||||
iam_client = client("iam")
|
||||
policy_name = "policy_list_scoped_wildcards_only"
|
||||
policy_document = {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": [
|
||||
"arn:aws:iam::*:role/team-*",
|
||||
"arn:aws:iam::*:role/dev-*",
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
arn = iam_client.create_policy(
|
||||
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
|
||||
)["Policy"]["Arn"]
|
||||
|
||||
from prowler.providers.aws.services.iam.iam_service import IAM
|
||||
|
||||
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.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
|
||||
new=IAM(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption import (
|
||||
iam_no_custom_policy_permissive_role_assumption,
|
||||
)
|
||||
|
||||
check = iam_no_custom_policy_permissive_role_assumption()
|
||||
result = check.execute()
|
||||
assert result[0].status == "FAIL"
|
||||
assert result[0].resource_arn == arn
|
||||
assert search(
|
||||
"allows permissive STS Role assumption", result[0].status_extended
|
||||
)
|
||||
|
||||
@@ -91,6 +91,21 @@ class Test_monitor_diagnostic_settings_exists:
|
||||
)
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
BlobProperties,
|
||||
DeleteRetentionPolicy,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
|
||||
# Create a valid BlobProperties instance
|
||||
valid_blob_properties = BlobProperties(
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version="default_service_version",
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=False, days=0
|
||||
),
|
||||
versioning_enabled=True,
|
||||
)
|
||||
|
||||
monitor_client.diagnostics_settings = {
|
||||
@@ -138,42 +153,34 @@ class Test_monitor_diagnostic_settings_exists:
|
||||
name="storageaccountname1",
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=True,
|
||||
infrastructure_encryption="Enabled",
|
||||
infrastructure_encryption=True,
|
||||
allow_blob_public_access=True,
|
||||
network_rule_set="AllowAll",
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="Microsoft.CustomerManagedKeyVault",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=365,
|
||||
key_expiration_period_in_days="365",
|
||||
location="euwest",
|
||||
blob_properties=mock.MagicMock(
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version="default_service_version",
|
||||
container_delete_retention_policy="container_delete_retention_policy",
|
||||
),
|
||||
blob_properties=valid_blob_properties,
|
||||
),
|
||||
Account(
|
||||
id="/subscriptions/1224a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname2",
|
||||
name="storageaccountname2",
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption="Enabled",
|
||||
infrastructure_encryption=True,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set="AllowAll",
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="Microsoft.Storage",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=365,
|
||||
key_expiration_period_in_days="365",
|
||||
location="euwest",
|
||||
blob_properties=mock.MagicMock(
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version="default_service_version",
|
||||
container_delete_retention_policy="container_delete_retention_policy",
|
||||
),
|
||||
blob_properties=valid_blob_properties,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -78,6 +78,9 @@ class Test_monitor_storage_account_with_activity_logs_cmk_encrypted:
|
||||
)
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
BlobProperties,
|
||||
DeleteRetentionPolicy,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
|
||||
monitor_client.diagnostics_settings = {
|
||||
@@ -125,20 +128,25 @@ class Test_monitor_storage_account_with_activity_logs_cmk_encrypted:
|
||||
name="storageaccountname1",
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=True,
|
||||
infrastructure_encryption="Enabled",
|
||||
infrastructure_encryption=True, # bool
|
||||
allow_blob_public_access=True,
|
||||
network_rule_set="AllowAll",
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="Microsoft.CustomerManagedKeyVault",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=365,
|
||||
key_expiration_period_in_days="365", # str
|
||||
location="euwest",
|
||||
blob_properties=mock.MagicMock(
|
||||
blob_properties=BlobProperties(
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version="default_service_version",
|
||||
container_delete_retention_policy="container_delete_retention_policy",
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=True, days=7
|
||||
),
|
||||
versioning_enabled=True,
|
||||
),
|
||||
),
|
||||
Account(
|
||||
@@ -146,20 +154,25 @@ class Test_monitor_storage_account_with_activity_logs_cmk_encrypted:
|
||||
name="storageaccountname2",
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption="Enabled",
|
||||
infrastructure_encryption=True, # bool
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set="AllowAll",
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="Microsoft.Storage",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=365,
|
||||
key_expiration_period_in_days="365", # str
|
||||
location="euwest",
|
||||
blob_properties=mock.MagicMock(
|
||||
blob_properties=BlobProperties(
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version="default_service_version",
|
||||
container_delete_retention_policy="container_delete_retention_policy",
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=True, days=7
|
||||
),
|
||||
versioning_enabled=False,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -78,6 +78,9 @@ class Test_monitor_storage_account_with_activity_logs_is_private:
|
||||
)
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
BlobProperties,
|
||||
DeleteRetentionPolicy,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
|
||||
monitor_client.diagnostics_settings = {
|
||||
@@ -125,20 +128,25 @@ class Test_monitor_storage_account_with_activity_logs_is_private:
|
||||
name="storageaccountname1",
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=True,
|
||||
infrastructure_encryption="Enabled",
|
||||
infrastructure_encryption=True,
|
||||
allow_blob_public_access=True,
|
||||
network_rule_set="AllowAll",
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="Microsoft.Storage",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=365,
|
||||
location="euwest",
|
||||
blob_properties=mock.MagicMock(
|
||||
blob_properties=BlobProperties(
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version="default_service_version",
|
||||
container_delete_retention_policy="container_delete_retention_policy",
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=True, days=7
|
||||
),
|
||||
versioning_enabled=True,
|
||||
),
|
||||
),
|
||||
Account(
|
||||
@@ -146,20 +154,25 @@ class Test_monitor_storage_account_with_activity_logs_is_private:
|
||||
name="storageaccountname2",
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption="Enabled",
|
||||
infrastructure_encryption=True,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set="AllowAll",
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="Microsoft.Storage",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=365,
|
||||
location="euwest",
|
||||
blob_properties=mock.MagicMock(
|
||||
blob_properties=BlobProperties(
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version="default_service_version",
|
||||
container_delete_retention_policy="container_delete_retention_policy",
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=True, days=7
|
||||
),
|
||||
versioning_enabled=False,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -40,16 +43,18 @@ class Test_storage_account_key_access_disabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
allow_blob_public_access=True,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
allow_shared_key_access=True,
|
||||
)
|
||||
]
|
||||
@@ -91,16 +96,18 @@ class Test_storage_account_key_access_disabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
allow_shared_key_access=False,
|
||||
)
|
||||
]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -40,16 +43,18 @@ class Test_storage_blob_public_access_level_is_disabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
allow_blob_public_access=True,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -90,16 +95,18 @@ class Test_storage_blob_public_access_level_is_disabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -45,23 +45,28 @@ class Test_storage_blob_versioning_is_enabled:
|
||||
new=storage_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
|
||||
storage_client.storage_accounts = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
blob_properties=storage_account_blob_properties,
|
||||
)
|
||||
]
|
||||
@@ -92,12 +97,13 @@ class Test_storage_blob_versioning_is_enabled:
|
||||
Account,
|
||||
BlobProperties,
|
||||
DeleteRetentionPolicy,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
|
||||
storage_account_blob_properties = BlobProperties(
|
||||
id=None,
|
||||
name=None,
|
||||
type=None,
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version=None,
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=False, days=0
|
||||
@@ -109,16 +115,18 @@ class Test_storage_blob_versioning_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
blob_properties=storage_account_blob_properties,
|
||||
)
|
||||
]
|
||||
@@ -158,12 +166,13 @@ class Test_storage_blob_versioning_is_enabled:
|
||||
Account,
|
||||
BlobProperties,
|
||||
DeleteRetentionPolicy,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
|
||||
storage_account_blob_properties = BlobProperties(
|
||||
id=None,
|
||||
name=None,
|
||||
type=None,
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version=None,
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=False, days=0
|
||||
@@ -175,16 +184,18 @@ class Test_storage_blob_versioning_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
blob_properties=storage_account_blob_properties,
|
||||
)
|
||||
]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -40,16 +43,18 @@ class Test_storage_cross_tenant_replication_disabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
allow_cross_tenant_replication=True,
|
||||
)
|
||||
]
|
||||
@@ -91,16 +96,18 @@ class Test_storage_cross_tenant_replication_disabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
allow_cross_tenant_replication=False,
|
||||
)
|
||||
]
|
||||
|
||||
@@ -43,18 +43,18 @@ class Test_storage_default_network_access_rule_is_denied:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
default_action="Allow", bypass="AzureServices"
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -95,18 +95,18 @@ class Test_storage_default_network_access_rule_is_denied:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
default_action="Deny", bypass="AzureServices"
|
||||
),
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -40,16 +43,18 @@ class Test_storage_default_to_entra_authorization_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
default_to_entra_authorization=True,
|
||||
)
|
||||
]
|
||||
@@ -91,16 +96,18 @@ class Test_storage_default_to_entra_authorization_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
default_to_entra_authorization=False,
|
||||
)
|
||||
]
|
||||
|
||||
@@ -43,18 +43,18 @@ class Test_storage_ensure_azure_services_are_trusted_to_access_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="None", default_action="Deny"
|
||||
),
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -95,18 +95,18 @@ class Test_storage_ensure_azure_services_are_trusted_to_access_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -40,16 +43,18 @@ class Test_storage_ensure_encryption_with_customer_managed_keys:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -90,16 +95,18 @@ class Test_storage_ensure_encryption_with_customer_managed_keys:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
encryption_type="Microsoft.Keyvault",
|
||||
minimum_tls_version=None,
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
DeleteRetentionPolicy,
|
||||
FileServiceProperties,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
@@ -44,16 +45,18 @@ class Test_storage_ensure_file_shares_soft_delete_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
file_service_properties=None,
|
||||
)
|
||||
]
|
||||
@@ -93,16 +96,18 @@ class Test_storage_ensure_file_shares_soft_delete_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
file_service_properties=file_service_properties,
|
||||
)
|
||||
]
|
||||
@@ -151,16 +156,18 @@ class Test_storage_ensure_file_shares_soft_delete_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
file_service_properties=file_service_properties,
|
||||
)
|
||||
]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -40,16 +43,18 @@ class Test_storage_ensure_minimum_tls_version_12:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_1",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -90,16 +95,18 @@ class Test_storage_ensure_minimum_tls_version_12:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
PrivateEndpointConnection,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
@@ -45,16 +46,18 @@ class Test_storage_ensure_private_endpoints_in_storage_accounts:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -97,22 +100,24 @@ class Test_storage_ensure_private_endpoints_in_storage_accounts:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=PrivateEndpointConnection(
|
||||
id=str(
|
||||
uuid4(),
|
||||
),
|
||||
name="Test Private Endpoint Connection",
|
||||
type="Test Type",
|
||||
),
|
||||
private_endpoint_connections=[
|
||||
PrivateEndpointConnection(
|
||||
id="f1ef2e48-978a-4b0e-b34f-e6c34a9e0724",
|
||||
name="Test Private Endpoint Connection",
|
||||
type="Test Type",
|
||||
)
|
||||
],
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
BlobProperties,
|
||||
DeleteRetentionPolicy,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
@@ -45,16 +46,18 @@ class Test_storage_ensure_soft_delete_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
blob_properties=storage_account_blob_properties,
|
||||
)
|
||||
]
|
||||
@@ -85,29 +88,32 @@ class Test_storage_ensure_soft_delete_is_enabled:
|
||||
storage_account_name = "Test Storage Account"
|
||||
storage_client = mock.MagicMock
|
||||
storage_account_blob_properties = BlobProperties(
|
||||
id=None,
|
||||
name=None,
|
||||
type=None,
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version=None,
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=False, days=7
|
||||
),
|
||||
versioning_enabled=False,
|
||||
)
|
||||
storage_client.storage_accounts = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
blob_properties=storage_account_blob_properties,
|
||||
)
|
||||
]
|
||||
@@ -147,29 +153,32 @@ class Test_storage_ensure_soft_delete_is_enabled:
|
||||
storage_account_name = "Test Storage Account"
|
||||
storage_client = mock.MagicMock
|
||||
storage_account_blob_properties = BlobProperties(
|
||||
id=None,
|
||||
name=None,
|
||||
type=None,
|
||||
id="id",
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version=None,
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=True, days=7
|
||||
),
|
||||
versioning_enabled=True,
|
||||
)
|
||||
storage_client.storage_accounts = {
|
||||
AZURE_SUBSCRIPTION_ID: [
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
blob_properties=storage_account_blob_properties,
|
||||
)
|
||||
]
|
||||
|
||||
@@ -3,6 +3,7 @@ from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
ReplicationSettings,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
@@ -43,16 +44,18 @@ class Test_storage_geo_redundant_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
replication_settings=ReplicationSettings.STANDARD_GRS,
|
||||
)
|
||||
]
|
||||
@@ -94,16 +97,18 @@ class Test_storage_geo_redundant_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=None,
|
||||
encryption_type=None,
|
||||
minimum_tls_version=None,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
replication_settings=ReplicationSettings.STANDARD_LRS,
|
||||
)
|
||||
]
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -40,16 +43,18 @@ class Test_storage_infrastructure_encryption_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_1",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -90,16 +95,18 @@ class Test_storage_infrastructure_encryption_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=True,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_1",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -41,16 +44,18 @@ class Test_storage_key_rotation_90_dayss:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
key_expiration_period_in_days="91",
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_1",
|
||||
key_expiration_period_in_days=expiration_days,
|
||||
minimum_tls_version="TLS1_2",
|
||||
private_endpoint_connections=[],
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -92,16 +97,18 @@ class Test_storage_key_rotation_90_dayss:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
key_expiration_period_in_days=90,
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=expiration_days,
|
||||
private_endpoint_connections=[],
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -142,16 +149,18 @@ class Test_storage_key_rotation_90_dayss:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
key_expiration_period_in_days=None,
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
private_endpoint_connections=[],
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from prowler.providers.azure.services.storage.storage_service import Account
|
||||
from prowler.providers.azure.services.storage.storage_service import (
|
||||
Account,
|
||||
NetworkRuleSet,
|
||||
)
|
||||
from tests.providers.azure.azure_fixtures import (
|
||||
AZURE_SUBSCRIPTION_ID,
|
||||
set_mocked_azure_provider,
|
||||
@@ -40,16 +43,18 @@ class Test_storage_secure_transfer_required_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_1",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -90,16 +95,18 @@ class Test_storage_secure_transfer_required_is_enabled:
|
||||
Account(
|
||||
id=storage_account_id,
|
||||
name=storage_account_name,
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=True,
|
||||
infrastructure_encryption=True,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version="TLS1_1",
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
location="westeurope",
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ from prowler.providers.azure.services.storage.storage_service import (
|
||||
BlobProperties,
|
||||
DeleteRetentionPolicy,
|
||||
FileServiceProperties,
|
||||
NetworkRuleSet,
|
||||
ReplicationSettings,
|
||||
Storage,
|
||||
)
|
||||
@@ -20,7 +21,7 @@ def mock_storage_get_storage_accounts(_):
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version=None,
|
||||
container_delete_retention_policy=None,
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7),
|
||||
)
|
||||
retention_policy = DeleteRetentionPolicy(enabled=True, days=7)
|
||||
file_service_properties = FileServiceProperties(
|
||||
@@ -34,15 +35,17 @@ def mock_storage_get_storage_accounts(_):
|
||||
Account(
|
||||
id="id",
|
||||
name="name",
|
||||
resouce_group_name=None,
|
||||
resouce_group_name="rg",
|
||||
enable_https_traffic_only=False,
|
||||
infrastructure_encryption=False,
|
||||
allow_blob_public_access=None,
|
||||
network_rule_set=None,
|
||||
allow_blob_public_access=False,
|
||||
network_rule_set=NetworkRuleSet(
|
||||
bypass="AzureServices", default_action="Allow"
|
||||
),
|
||||
encryption_type="None",
|
||||
minimum_tls_version=None,
|
||||
minimum_tls_version="TLS1_2",
|
||||
key_expiration_period_in_days=None,
|
||||
private_endpoint_connections=None,
|
||||
private_endpoint_connections=[],
|
||||
location="westeurope",
|
||||
blob_properties=blob_properties,
|
||||
default_to_entra_authorization=True,
|
||||
@@ -77,7 +80,7 @@ class Test_Storage_Service:
|
||||
assert storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].name == "name"
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].resouce_group_name
|
||||
is None
|
||||
== "rg"
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].enable_https_traffic_only
|
||||
@@ -89,10 +92,21 @@ class Test_Storage_Service:
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].allow_blob_public_access
|
||||
is None
|
||||
is False
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].network_rule_set is None
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].network_rule_set
|
||||
is not None
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].network_rule_set.bypass
|
||||
== "AzureServices"
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
|
||||
0
|
||||
].network_rule_set.default_action
|
||||
== "Allow"
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].encryption_type == "None"
|
||||
@@ -102,7 +116,7 @@ class Test_Storage_Service:
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].minimum_tls_version
|
||||
is None
|
||||
== "TLS1_2"
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
|
||||
@@ -114,7 +128,7 @@ class Test_Storage_Service:
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
|
||||
0
|
||||
].private_endpoint_connections
|
||||
is None
|
||||
== []
|
||||
)
|
||||
assert storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
|
||||
0
|
||||
@@ -123,7 +137,9 @@ class Test_Storage_Service:
|
||||
name="name",
|
||||
type="type",
|
||||
default_service_version=None,
|
||||
container_delete_retention_policy=None,
|
||||
container_delete_retention_policy=DeleteRetentionPolicy(
|
||||
enabled=True, days=7
|
||||
),
|
||||
)
|
||||
assert storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
|
||||
0
|
||||
@@ -173,7 +189,19 @@ class Test_Storage_Service:
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
|
||||
0
|
||||
].blob_properties.container_delete_retention_policy
|
||||
is None
|
||||
is not None
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
|
||||
0
|
||||
].blob_properties.container_delete_retention_policy.enabled
|
||||
is True
|
||||
)
|
||||
assert (
|
||||
storage.storage_accounts[AZURE_SUBSCRIPTION_ID][
|
||||
0
|
||||
].blob_properties.container_delete_retention_policy.days
|
||||
== 7
|
||||
)
|
||||
|
||||
def test_get_file_service_properties(self):
|
||||
|
||||
@@ -7,6 +7,17 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
### 🚀 Added
|
||||
### 🔄 Changed
|
||||
### 🐞 Fixed
|
||||
### Removed
|
||||
|
||||
---
|
||||
|
||||
## [v1.8.1] (Prowler 5.8.1)
|
||||
|
||||
### 🔄 Changed
|
||||
- Latest new failed findings now use `GET /findings/latest` [(#8219)](https://github.com/prowler-cloud/prowler/pull/8219)
|
||||
|
||||
### Removed
|
||||
- Validation of the provider's secret type during updates [(#8197)](https://github.com/prowler-cloud/prowler/pull/8197)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
} from "@/lib";
|
||||
import {
|
||||
buildSecretConfig,
|
||||
buildUpdateSecretConfig,
|
||||
handleApiError,
|
||||
handleApiResponse,
|
||||
} from "@/lib/provider-credentials/build-crendentials";
|
||||
@@ -194,8 +193,7 @@ export const updateCredentialsProvider = async (
|
||||
) as ProviderType;
|
||||
|
||||
try {
|
||||
const secret = buildUpdateSecretConfig(formData, providerType);
|
||||
|
||||
const { secretType, secret } = buildSecretConfig(formData, providerType);
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "PATCH",
|
||||
headers,
|
||||
@@ -203,7 +201,7 @@ export const updateCredentialsProvider = async (
|
||||
data: {
|
||||
type: "provider-secrets",
|
||||
id: credentialsId,
|
||||
attributes: { secret },
|
||||
attributes: { secret_type: secretType, secret },
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Spacer } from "@nextui-org/react";
|
||||
import { format, subDays } from "date-fns";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getFindings } from "@/actions/findings/findings";
|
||||
import { getLatestFindings } from "@/actions/findings/findings";
|
||||
import {
|
||||
getFindingsBySeverity,
|
||||
getFindingsByStatus,
|
||||
@@ -129,15 +128,12 @@ const SSRDataNewFindingsTable = async () => {
|
||||
const page = 1;
|
||||
const sort = "severity,-inserted_at";
|
||||
|
||||
const twoDaysAgo = format(subDays(new Date(), 2), "yyyy-MM-dd");
|
||||
|
||||
const defaultFilters = {
|
||||
"filter[status__in]": "FAIL",
|
||||
"filter[delta__in]": "new",
|
||||
"filter[inserted_at__gte]": twoDaysAgo,
|
||||
"filter[status]": "FAIL",
|
||||
"filter[delta]": "new",
|
||||
};
|
||||
|
||||
const findingsData = await getFindings({
|
||||
const findingsData = await getLatestFindings({
|
||||
query: undefined,
|
||||
page,
|
||||
sort,
|
||||
@@ -178,8 +174,7 @@ const SSRDataNewFindingsTable = async () => {
|
||||
Latest new failing findings
|
||||
</h3>
|
||||
<p className="text-xs text-gray-500">
|
||||
Showing the latest 10 new failing findings by severity from the last
|
||||
2 days.
|
||||
Showing the latest 10 new failing findings by severity.
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute -top-6 right-0">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Spacer } from "@nextui-org/react";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { getProvider, getProviders } from "@/actions/providers";
|
||||
import { getProviders } from "@/actions/providers";
|
||||
import { getScans, getScansByState } from "@/actions/scans";
|
||||
import {
|
||||
AutoRefresh,
|
||||
@@ -45,13 +45,9 @@ export default async function Scans({
|
||||
connected: provider.attributes.connection.connected,
|
||||
})) || [];
|
||||
|
||||
const providersCountConnected = await getProviders({
|
||||
filters: { "filter[connected]": true },
|
||||
pageSize: 50,
|
||||
});
|
||||
const thereIsNoProviders = !providersCountConnected?.data;
|
||||
const thereIsNoProviders = !providersData?.data;
|
||||
|
||||
const thereIsNoProvidersConnected = providersCountConnected?.data?.every(
|
||||
const thereIsNoProvidersConnected = providersData?.data?.every(
|
||||
(provider: ProviderProps) => !provider.attributes.connection.connected,
|
||||
);
|
||||
|
||||
@@ -123,34 +119,43 @@ const SSRDataTableScans = async ({
|
||||
// Extract query from filters
|
||||
const query = (filters["filter[search]"] as string) || "";
|
||||
|
||||
// Fetch scans data
|
||||
const scansData = await getScans({ query, page, sort, filters, pageSize });
|
||||
// Fetch scans data with provider information included
|
||||
const scansData = await getScans({
|
||||
query,
|
||||
page,
|
||||
sort,
|
||||
filters,
|
||||
pageSize,
|
||||
include: "provider",
|
||||
});
|
||||
|
||||
// Handle expanded scans data
|
||||
const expandedScansData = await Promise.all(
|
||||
scansData?.data?.map(async (scan: any) => {
|
||||
// Process scans with provider information from included data
|
||||
const expandedScansData =
|
||||
scansData?.data?.map((scan: any) => {
|
||||
const providerId = scan.relationships?.provider?.data?.id;
|
||||
|
||||
if (!providerId) {
|
||||
return { ...scan, providerInfo: null };
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("id", providerId);
|
||||
// Find the provider data in the included array
|
||||
const providerData = scansData.included?.find(
|
||||
(item: any) => item.type === "providers" && item.id === providerId,
|
||||
);
|
||||
|
||||
const providerData = await getProvider(formData);
|
||||
|
||||
if (providerData?.data) {
|
||||
const { provider, uid, alias } = providerData.data.attributes;
|
||||
return {
|
||||
...scan,
|
||||
providerInfo: { provider, uid, alias },
|
||||
};
|
||||
if (!providerData) {
|
||||
return { ...scan, providerInfo: null };
|
||||
}
|
||||
|
||||
return { ...scan, providerInfo: null };
|
||||
}) || [],
|
||||
);
|
||||
return {
|
||||
...scan,
|
||||
providerInfo: {
|
||||
provider: providerData.attributes.provider,
|
||||
uid: providerData.attributes.uid,
|
||||
alias: providerData.attributes.alias,
|
||||
},
|
||||
};
|
||||
}) || [];
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { InfoIcon } from "@/components/icons";
|
||||
import { SelectViaAWS } from "@/components/providers/workflow/forms/select-credentials-type/aws";
|
||||
import { SelectViaGCP } from "@/components/providers/workflow/forms/select-credentials-type/gcp";
|
||||
import { ProviderType } from "@/types/providers";
|
||||
@@ -24,28 +23,5 @@ export const CredentialsUpdateInfo = ({
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<p className="text-sm text-default-700">
|
||||
To update provider credentials,{" "}
|
||||
<strong>
|
||||
the same type that was originally configured must be used.
|
||||
</strong>
|
||||
</p>
|
||||
<div className="flex items-center rounded-lg border border-system-warning bg-system-warning-medium p-4 text-sm dark:text-default-300">
|
||||
<InfoIcon className="mr-2 inline h-4 w-4 flex-shrink-0" />
|
||||
<p>
|
||||
If the provider was configured with static credentials, updates must
|
||||
also use static credentials. If it was configured with a role in AWS
|
||||
(or service account in GCP),{" "}
|
||||
<strong>updates must use the same type.</strong>
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-sm font-bold text-default-700">
|
||||
To switch from one type to another, the provider must be deleted and set
|
||||
up again.
|
||||
</p>
|
||||
{renderSelectComponent()}
|
||||
</div>
|
||||
);
|
||||
return <div className="flex flex-col gap-4">{renderSelectComponent()}</div>;
|
||||
};
|
||||
|
||||
@@ -121,44 +121,44 @@ export const useRelatedFilters = ({
|
||||
|
||||
if (shouldDeselectScan) {
|
||||
updateFilter(FilterType.SCAN, null);
|
||||
} else {
|
||||
// Add provider if not already selected
|
||||
if (scanProviderId && !currentProviders.includes(scanProviderId)) {
|
||||
updateFilter(FilterType.PROVIDER_UID, [
|
||||
...currentProviders,
|
||||
scanProviderId,
|
||||
]);
|
||||
}
|
||||
// } else {
|
||||
// // Add provider if not already selected
|
||||
// if (scanProviderId && !currentProviders.includes(scanProviderId)) {
|
||||
// updateFilter(FilterType.PROVIDER_UID, [
|
||||
// ...currentProviders,
|
||||
// scanProviderId,
|
||||
// ]);
|
||||
// }
|
||||
|
||||
// Only add provider type if there are none selected
|
||||
if (
|
||||
scanProviderType &&
|
||||
currentProviderTypes.length === 0 &&
|
||||
!isManualDeselection.current
|
||||
) {
|
||||
updateFilter(FilterType.PROVIDER_TYPE, [scanProviderType]);
|
||||
}
|
||||
// // Only add provider type if there are none selected
|
||||
// if (
|
||||
// scanProviderType &&
|
||||
// currentProviderTypes.length === 0 &&
|
||||
// !isManualDeselection.current
|
||||
// ) {
|
||||
// updateFilter(FilterType.PROVIDER_TYPE, [scanProviderType]);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// Handle provider selection logic
|
||||
if (
|
||||
currentProviders.length > 0 &&
|
||||
deselectedProviders.length === 0 &&
|
||||
!isManualDeselection.current
|
||||
) {
|
||||
const providerTypes = currentProviders
|
||||
.map(getProviderType)
|
||||
.filter((type): type is ProviderType => type !== null);
|
||||
const selectedProviderTypes = Array.from(new Set(providerTypes));
|
||||
// // Handle provider selection logic
|
||||
// if (
|
||||
// currentProviders.length > 0 &&
|
||||
// deselectedProviders.length === 0 &&
|
||||
// !isManualDeselection.current
|
||||
// ) {
|
||||
// const providerTypes = currentProviders
|
||||
// .map(getProviderType)
|
||||
// .filter((type): type is ProviderType => type !== null);
|
||||
// const selectedProviderTypes = Array.from(new Set(providerTypes));
|
||||
|
||||
if (
|
||||
selectedProviderTypes.length > 0 &&
|
||||
currentProviderTypes.length === 0
|
||||
) {
|
||||
updateFilter(FilterType.PROVIDER_TYPE, selectedProviderTypes);
|
||||
}
|
||||
}
|
||||
// if (
|
||||
// selectedProviderTypes.length > 0 &&
|
||||
// currentProviderTypes.length === 0
|
||||
// ) {
|
||||
// updateFilter(FilterType.PROVIDER_TYPE, selectedProviderTypes);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Update available providers
|
||||
if (currentProviderTypes.length > 0) {
|
||||
|
||||
@@ -187,25 +187,6 @@ export const buildSecretConfig = (
|
||||
return builder();
|
||||
};
|
||||
|
||||
// Helper function to build secret for update (reuses existing logic)
|
||||
export const buildUpdateSecretConfig = (
|
||||
formData: FormData,
|
||||
providerType: ProviderType,
|
||||
) => {
|
||||
// Reuse the same secret building logic as add, but only return the secret
|
||||
const { secret } = buildSecretConfig(formData, providerType);
|
||||
|
||||
// Handle special case for M365 password field inconsistency
|
||||
if (providerType === "m365") {
|
||||
return {
|
||||
...secret,
|
||||
password: formData.get(ProviderCredentialFields.PASSWORD),
|
||||
};
|
||||
}
|
||||
|
||||
return secret;
|
||||
};
|
||||
|
||||
// Helper function to handle API responses consistently
|
||||
export const handleApiResponse = async (
|
||||
response: Response,
|
||||
|
||||
Reference in New Issue
Block a user