From 28e8f0de2b31b7a21b2364ee6f768fde9d12753d Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Thu, 21 Mar 2024 17:08:19 +0100 Subject: [PATCH] chore(merge): get latest changes from v3 to v4 (#3582) Co-authored-by: Hugo966 <148140670+Hugo966@users.noreply.github.com> Co-authored-by: Pepe Fagoaga Co-authored-by: Nacho Rivera --- .github/workflows/pypi-release.yml | 114 +++++++--- .../providers/aws/aws_regions_by_service.json | 2 +- ...i_operations_cloudtrail_logging_enabled.py | 2 +- .../cloudtrail_bucket_requires_mfa_delete.py | 2 +- .../cloudtrail_cloudwatch_logging_enabled.py | 2 +- .../cloudtrail_insights_exist.py | 2 +- .../cloudtrail_kms_encryption_enabled.py | 2 +- .../cloudtrail_log_file_validation_enabled.py | 2 +- ...l_logs_s3_bucket_access_logging_enabled.py | 2 +- ...gs_s3_bucket_is_not_publicly_accessible.py | 2 +- .../cloudtrail_multi_region_enabled.py | 4 +- ...egion_enabled_logging_management_events.py | 2 +- .../cloudtrail_s3_dataevents_read_enabled.py | 2 +- .../cloudtrail_s3_dataevents_write_enabled.py | 2 +- .../services/cloudtrail/cloudtrail_service.py | 44 ++-- .../services/cloudwatch/lib/metric_filters.py | 2 +- .../app/app_http_logs_enabled/__init__.py | 0 .../app_http_logs_enabled.metadata.json | 30 +++ .../app_http_logs_enabled.py | 29 +++ .../azure/services/app/app_service.py | 23 ++ .../azure/services/monitor/monitor_service.py | 8 +- ...udtrail_cloudwatch_logging_enabled_test.py | 10 +- .../cloudtrail/cloudtrail_service_test.py | 8 +- .../app_client_certificates_on_test.py | 51 ++--- .../app_ensure_auth_is_set_up_test.py | 51 ++--- ...ensure_http_is_redirected_to_https_test.py | 51 ++--- .../app_ensure_java_version_is_latest_test.py | 171 ++++++++------- .../app_ensure_php_version_is_latest_test.py | 76 +++---- ...pp_ensure_python_version_is_latest_test.py | 76 +++---- .../app_ensure_using_http20_test.py | 76 +++---- .../app_ftp_deployment_disabled_test.py | 76 +++---- .../app_http_logs_enabled_test.py | 207 ++++++++++++++++++ .../app_minimum_tls_version_12_test.py | 76 +++---- .../app_register_with_identity_test.py | 51 ++--- .../azure/services/app/app_service_test.py | 186 ++++++++++++---- 35 files changed, 957 insertions(+), 487 deletions(-) create mode 100644 prowler/providers/azure/services/app/app_http_logs_enabled/__init__.py create mode 100644 prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.metadata.json create mode 100644 prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.py create mode 100644 tests/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled_test.py diff --git a/.github/workflows/pypi-release.yml b/.github/workflows/pypi-release.yml index 53289f1c2e..12928876a5 100644 --- a/.github/workflows/pypi-release.yml +++ b/.github/workflows/pypi-release.yml @@ -6,7 +6,13 @@ on: env: RELEASE_TAG: ${{ github.event.release.tag_name }} - GITHUB_BRANCH: master + PYTHON_VERSION: 3.11 + CACHE: "poetry" + # This base branch is used to create a PR with the updated version + # We'd need to handle the base branch for v4 and v3, since they will be + # `master` and `3.0-dev`, respectively + GITHUB_BASE_BRANCH: "master" + GIT_COMMITTER_EMAIL: "sergio@prowler.com" jobs: release-prowler-job: @@ -15,56 +21,110 @@ jobs: POETRY_VIRTUALENVS_CREATE: "false" name: Release Prowler to PyPI steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - name: Get base branch regarding Prowler version + run: | + PROWLER_VERSION="${{ env.RELEASE_TAG }}" + BASE_BRANCH="" + + case ${PROWLER_VERSION%%.*} in + 3) + echo "Releasing Prowler v3 with tag ${PROWLER_VERSION}" + # TODO: modify it once v4 is ready + # echo "BASE_BRANCH=3.0-dev" >> "${GITHUB_ENV}" + echo "BASE_BRANCH=master" >> "${GITHUB_ENV}" + ;; + 4) + echo "Releasing Prowler v4 with tag ${PROWLER_VERSION}" + # TODO: modify it once v4 is ready + # echo "BASE_BRANCH=master" >> "${GITHUB_ENV}" + echo "Not available, aborting..." + exit 1 + ;; + *) + echo "Releasing another Prowler major version, aborting..." + exit 1 + ;; + esac + - uses: actions/checkout@v4 - with: - ref: ${{ env.GITHUB_BRANCH }} + - name: Install dependencies run: | pipx install poetry pipx inject poetry poetry-bumpversion - - name: setup python + + - name: Setup Python uses: actions/setup-python@v5 with: - python-version: 3.9 - cache: 'poetry' - - name: Change version and Build package + python-version: ${{ env.PYTHON_VERSION }} + cache: ${{ env.CACHE }} + + - name: Update Poetry and config version run: | poetry version ${{ env.RELEASE_TAG }} + + - name: Import GPG key + uses: crazy-max/ghaction-import-gpg@v4 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + + - name: Push updated version to the release tag + run: | + # Configure Git git config user.name "github-actions" - git config user.email "" + git config user.email "${{ env.GIT_COMMITTER_EMAIL }}" + + # Add the files with the version changed git add prowler/config/config.py pyproject.toml - git commit -m "chore(release): ${{ env.RELEASE_TAG }}" --no-verify - git tag -fa ${{ env.RELEASE_TAG }} -m "chore(release): ${{ env.RELEASE_TAG }}" + git commit -m "chore(release): ${{ env.RELEASE_TAG }}" --no-verify -S + + # Replace the tag with the version updated + git tag -fa ${{ env.RELEASE_TAG }} -m "chore(release): ${{ env.RELEASE_TAG }}" --sign + + # Push the tag git push -f origin ${{ env.RELEASE_TAG }} + + - name: Create new branch for the version update + run: | + git switch -c release-${{ env.RELEASE_TAG }} + git push --set-upstream origin release-${{ env.RELEASE_TAG }} + + - name: Build Prowler package + run: | poetry build - - name: Publish prowler package to PyPI + + - name: Publish Prowler package to PyPI run: | poetry config pypi-token.pypi ${{ secrets.PYPI_API_TOKEN }} poetry publish - # Create pull request with new version - - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 - with: - token: ${{ secrets.PROWLER_ACCESS_TOKEN }} - commit-message: "chore(release): update Prowler Version to ${{ env.RELEASE_TAG }}." - branch: release-${{ env.RELEASE_TAG }} - labels: "status/waiting-for-revision, severity/low" - title: "chore(release): update Prowler Version to ${{ env.RELEASE_TAG }}" - body: | - ### Description - This PR updates Prowler Version to ${{ env.RELEASE_TAG }}. + - name: Create PR to update version in the branch + run: | + echo "### Description - ### License + This PR updates Prowler Version to ${{ env.RELEASE_TAG }}. - By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. - - name: Replicate PyPi Package + ### License + + By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license." |\ + gh pr create \ + --base ${{ env.BASE_BRANCH }} \ + --head release-${{ env.RELEASE_TAG }} \ + --title "chore(release): update Prowler Version to ${{ env.RELEASE_TAG }}." \ + --body-file - + env: + GH_TOKEN: ${{ secrets.PROWLER_ACCESS_TOKEN }} + + - name: Replicate PyPI package run: | rm -rf ./dist && rm -rf ./build && rm -rf prowler.egg-info pip install toml python util/replicate_pypi_package.py poetry build + - name: Publish prowler-cloud package to PyPI run: | poetry config pypi-token.pypi ${{ secrets.PYPI_API_TOKEN }} diff --git a/prowler/providers/aws/aws_regions_by_service.json b/prowler/providers/aws/aws_regions_by_service.json index fc5e83e91c..08e9032bf8 100644 --- a/prowler/providers/aws/aws_regions_by_service.json +++ b/prowler/providers/aws/aws_regions_by_service.json @@ -7046,6 +7046,7 @@ "ap-east-1", "ap-northeast-1", "ap-northeast-2", + "ap-northeast-3", "ap-south-1", "ap-southeast-1", "ap-southeast-2", @@ -9969,7 +9970,6 @@ "support": { "regions": { "aws": [ - "af-south-1", "ap-east-1", "ap-northeast-1", "ap-northeast-2", diff --git a/prowler/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.py b/prowler/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.py index 9ee86439ff..22ee6aaa31 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.py +++ b/prowler/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.py @@ -20,7 +20,7 @@ class awslambda_function_invoke_api_operations_cloudtrail_logging_enabled(Check) f"Lambda function {function.name} is not recorded by CloudTrail." ) lambda_recorded_cloudtrail = False - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): for data_event in trail.data_events: # classic event selectors if not data_event.is_advanced: diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete.py index 92b0edc1b1..786d9f7a62 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_bucket_requires_mfa_delete/cloudtrail_bucket_requires_mfa_delete.py @@ -8,7 +8,7 @@ from prowler.providers.aws.services.s3.s3_client import s3_client class cloudtrail_bucket_requires_mfa_delete(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): if trail.is_logging: trail_bucket_is_in_account = False trail_bucket = trail.s3_bucket diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.py index d4058847d5..94c7381c9e 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled.py @@ -11,7 +11,7 @@ maximum_time_without_logging = 1 class cloudtrail_cloudwatch_logging_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): if trail.name: report = Check_Report_AWS(self.metadata()) report.region = trail.region diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist.py index 59972e148b..a175c4fa1f 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_insights_exist/cloudtrail_insights_exist.py @@ -7,7 +7,7 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import ( class cloudtrail_insights_exist(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): if trail.is_logging: report = Check_Report_AWS(self.metadata()) report.region = trail.region diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled.py index 3a02af5edf..68b4b0c7d5 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_kms_encryption_enabled/cloudtrail_kms_encryption_enabled.py @@ -7,7 +7,7 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import ( class cloudtrail_kms_encryption_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): if trail.name: report = Check_Report_AWS(self.metadata()) report.region = trail.region diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled.py index 1d63eb64e1..f7aa0fbf75 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_log_file_validation_enabled/cloudtrail_log_file_validation_enabled.py @@ -7,7 +7,7 @@ from prowler.providers.aws.services.cloudtrail.cloudtrail_client import ( class cloudtrail_log_file_validation_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): if trail.name: report = Check_Report_AWS(self.metadata()) report.region = trail.region diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled.py index 74120b782a..95f0557850 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_access_logging_enabled/cloudtrail_logs_s3_bucket_access_logging_enabled.py @@ -8,7 +8,7 @@ from prowler.providers.aws.services.s3.s3_client import s3_client class cloudtrail_logs_s3_bucket_access_logging_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): if trail.name: trail_bucket_is_in_account = False trail_bucket = trail.s3_bucket diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible.py index 3b6fc76646..258977522b 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_logs_s3_bucket_is_not_publicly_accessible/cloudtrail_logs_s3_bucket_is_not_publicly_accessible.py @@ -8,7 +8,7 @@ from prowler.providers.aws.services.s3.s3_client import s3_client class cloudtrail_logs_s3_bucket_is_not_publicly_accessible(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): if trail.name: trail_bucket_is_in_account = False trail_bucket = trail.s3_bucket diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled.py index 14900819ff..17646fef78 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled/cloudtrail_multi_region_enabled.py @@ -10,8 +10,8 @@ class cloudtrail_multi_region_enabled(Check): for region in cloudtrail_client.regional_clients.keys(): report = Check_Report_AWS(self.metadata()) report.region = region - for trail in cloudtrail_client.trails: - if trail.region == region: + for trail in cloudtrail_client.trails.values(): + if trail.region == region or trail.is_multiregion: if trail.is_logging: report.status = "PASS" report.resource_id = trail.name diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events.py index eef88a2c39..e97159d4b1 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_multi_region_enabled_logging_management_events/cloudtrail_multi_region_enabled_logging_management_events.py @@ -16,7 +16,7 @@ class cloudtrail_multi_region_enabled_logging_management_events(Check): report.resource_id = cloudtrail_client.audited_account report.resource_arn = cloudtrail_client.trail_arn_template - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): if trail.is_logging: if trail.is_multiregion: for event in trail.data_events: diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled.py index dc0916f34c..e26e2a8eab 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_read_enabled/cloudtrail_s3_dataevents_read_enabled.py @@ -8,7 +8,7 @@ from prowler.providers.aws.services.s3.s3_client import s3_client class cloudtrail_s3_dataevents_read_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): for data_event in trail.data_events: # classic event selectors if not data_event.is_advanced: diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled.py index fda427d25f..7863eaf02b 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_s3_dataevents_write_enabled/cloudtrail_s3_dataevents_write_enabled.py @@ -8,7 +8,7 @@ from prowler.providers.aws.services.s3.s3_client import s3_client class cloudtrail_s3_dataevents_write_enabled(Check): def execute(self): findings = [] - for trail in cloudtrail_client.trails: + for trail in cloudtrail_client.trails.values(): for data_event in trail.data_events: # Classic event selectors if not data_event.is_advanced: diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py index 8e92594991..71d2cfc8fc 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py @@ -15,7 +15,7 @@ class Cloudtrail(AWSService): # Call AWSService's __init__ super().__init__(__class__.__name__, provider) self.trail_arn_template = f"arn:{self.audited_partition}:cloudtrail:{self.region}:{self.audited_account}:trail" - self.trails = [] + self.trails = {} self.__threading_call__(self.__get_trails__) self.__get_trail_status__() self.__get_insight_selectors__() @@ -45,27 +45,23 @@ class Cloudtrail(AWSService): kms_key_id = trail["KmsKeyId"] if "CloudWatchLogsLogGroupArn" in trail: log_group_arn = trail["CloudWatchLogsLogGroupArn"] - self.trails.append( - Trail( - name=trail["Name"], - is_multiregion=trail["IsMultiRegionTrail"], - home_region=trail["HomeRegion"], - arn=trail["TrailARN"], - region=regional_client.region, - is_logging=False, - log_file_validation_enabled=trail[ - "LogFileValidationEnabled" - ], - latest_cloudwatch_delivery_time=None, - s3_bucket=trail["S3BucketName"], - kms_key=kms_key_id, - log_group_arn=log_group_arn, - data_events=[], - has_insight_selectors=trail.get("HasInsightSelectors"), - ) + self.trails[trail["TrailARN"]] = Trail( + name=trail["Name"], + is_multiregion=trail["IsMultiRegionTrail"], + home_region=trail["HomeRegion"], + arn=trail["TrailARN"], + region=regional_client.region, + is_logging=False, + log_file_validation_enabled=trail["LogFileValidationEnabled"], + latest_cloudwatch_delivery_time=None, + s3_bucket=trail["S3BucketName"], + kms_key=kms_key_id, + log_group_arn=log_group_arn, + data_events=[], + has_insight_selectors=trail.get("HasInsightSelectors"), ) if trails_count == 0: - self.trails.append( + self.trails[self.__get_trail_arn_template__(regional_client.region)] = ( Trail( region=regional_client.region, ) @@ -79,7 +75,7 @@ class Cloudtrail(AWSService): def __get_trail_status__(self): logger.info("Cloudtrail - Getting trail status") try: - for trail in self.trails: + for trail in self.trails.values(): for region, client in self.regional_clients.items(): if trail.region == region and trail.name: status = client.get_trail_status(Name=trail.arn) @@ -97,7 +93,7 @@ class Cloudtrail(AWSService): def __get_event_selectors__(self): logger.info("Cloudtrail - Getting event selector") try: - for trail in self.trails: + for trail in self.trails.values(): for region, client in self.regional_clients.items(): if trail.region == region and trail.name: data_events = client.get_event_selectors(TrailName=trail.arn) @@ -131,7 +127,7 @@ class Cloudtrail(AWSService): logger.info("Cloudtrail - Getting trail insight selectors...") try: - for trail in self.trails: + for trail in self.trails.values(): for region, client in self.regional_clients.items(): if trail.region == region and trail.name: insight_selectors = None @@ -180,7 +176,7 @@ class Cloudtrail(AWSService): def __list_tags_for_resource__(self): logger.info("CloudTrail - List Tags...") try: - for trail in self.trails: + for trail in self.trails.values(): # Check if trails are in this account and region if ( trail.region == trail.home_region diff --git a/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py b/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py index ea6dcafcd9..1c67b1d638 100644 --- a/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py +++ b/prowler/providers/aws/services/cloudwatch/lib/metric_filters.py @@ -12,7 +12,7 @@ def check_cloudwatch_log_metric_filter( ): # 1. Iterate for CloudWatch Log Group in CloudTrail trails log_groups = [] - for trail in trails: + for trail in trails.values(): if trail.log_group_arn: log_groups.append(trail.log_group_arn.split(":")[6]) # 2. Describe metric filters for previous log groups diff --git a/prowler/providers/azure/services/app/app_http_logs_enabled/__init__.py b/prowler/providers/azure/services/app/app_http_logs_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.metadata.json b/prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.metadata.json new file mode 100644 index 0000000000..2b58f1c7de --- /dev/null +++ b/prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "app_http_logs_enabled", + "CheckTitle": "Ensure that logging for Azure AppService 'HTTP logs' is enabled", + "CheckType": [], + "ServiceName": "app", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "low", + "ResourceType": "Microsoft.Web/sites/config", + "Description": "Enable AppServiceHTTPLogs diagnostic log category for Azure App Service instances to ensure all http requests are captured and centrally logged.", + "Risk": "Capturing web requests can be important supporting information for security analysts performing monitoring and incident response activities. Once logging, these logs can be ingested into SIEM or other central aggregation point for the organization.", + "RelatedUrl": "https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-logging-threat-detection#lt-3-enable-logging-for-security-investigation", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "https://docs.bridgecrew.io/docs/ensure-that-app-service-enables-http-logging#terraform" + }, + "Recommendation": { + "Text": "1. Go to App Services For each App Service: 2. Go to Diagnostic Settings 3. Click Add Diagnostic Setting 4. Check the checkbox next to 'HTTP logs' 5. Configure a destination based on your specific logging consumption capability (for example Stream to an event hub and then consuming with SIEM integration for Event Hub logging).", + "Url": "https://docs.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Log consumption and processing will incur additional cost." +} diff --git a/prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.py b/prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.py new file mode 100644 index 0000000000..6db1697909 --- /dev/null +++ b/prowler/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled.py @@ -0,0 +1,29 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.app.app_client import app_client + + +class app_http_logs_enabled(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + + for subscription_name, apps in app_client.apps.items(): + for app_name, app in apps.items(): + if "functionapp" not in app.kind: + report = Check_Report_Azure(self.metadata()) + report.status = "FAIL" + report.subscription = subscription_name + report.resource_name = app_name + report.resource_id = app.resource_id + if not app.monitor_diagnostic_settings: + report.status_extended = f"App {app_name} does not have a diagnostic setting in subscription {subscription_name}." + else: + for diagnostic_setting in app.monitor_diagnostic_settings: + report.status_extended = f"App {app_name} does not have HTTP Logs enabled in diagnostic setting {diagnostic_setting.name} in subscription {subscription_name}" + for log in diagnostic_setting.logs: + if log.category == "AppServiceHTTPLogs" and log.enabled: + report.status = "PASS" + report.status_extended = f"App {app_name} has HTTP Logs enabled in diagnostic setting {diagnostic_setting.name} in subscription {subscription_name}" + break + findings.append(report) + + return findings diff --git a/prowler/providers/azure/services/app/app_service.py b/prowler/providers/azure/services/app/app_service.py index b5e5911471..39af15891e 100644 --- a/prowler/providers/azure/services/app/app_service.py +++ b/prowler/providers/azure/services/app/app_service.py @@ -6,6 +6,8 @@ from azure.mgmt.web.models import ManagedServiceIdentity, SiteConfigResource from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider from prowler.providers.azure.lib.service.service import AzureService +from prowler.providers.azure.services.monitor.monitor_client import monitor_client +from prowler.providers.azure.services.monitor.monitor_service import DiagnosticSetting ########################## App @@ -49,8 +51,12 @@ class App(AzureService): getattr(app, "client_cert_enabled", False), getattr(app, "client_cert_mode", "Ignore"), ), + monitor_diagnostic_settings=self.__get_app_monitor_settings__( + app.name, app.resource_group, subscription_name + ), https_only=getattr(app, "https_only", False), identity=getattr(app, "identity", None), + kind=getattr(app, "kind", "app"), ) } ) @@ -78,6 +84,21 @@ class App(AzureService): return cert_mode + def __get_app_monitor_settings__(self, app_name, resource_group, subscription): + logger.info(f"App - Getting monitor diagnostics settings for {app_name}...") + monitor_diagnostics_settings = [] + try: + monitor_diagnostics_settings = monitor_client.diagnostic_settings_with_uri( + self.subscriptions[subscription], + f"subscriptions/{self.subscriptions[subscription]}/resourceGroups/{resource_group}/providers/Microsoft.Web/sites/{app_name}", + monitor_client.clients[subscription], + ) + except Exception as error: + logger.error( + f"Subscription name: {self.subscription} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return monitor_diagnostics_settings + @dataclass class WebApp: @@ -87,3 +108,5 @@ class WebApp: client_cert_mode: str = "Ignore" auth_enabled: bool = False https_only: bool = False + monitor_diagnostic_settings: list[DiagnosticSetting] = None + kind: str = "app" diff --git a/prowler/providers/azure/services/monitor/monitor_service.py b/prowler/providers/azure/services/monitor/monitor_service.py index f7480ae0a0..ed3b0ac557 100644 --- a/prowler/providers/azure/services/monitor/monitor_service.py +++ b/prowler/providers/azure/services/monitor/monitor_service.py @@ -43,14 +43,18 @@ class Monitor(AzureService): DiagnosticSetting( id=setting.id, name=setting.id.split("/")[-1], - storage_account_name=setting.storage_account_id.split("/")[-1], + storage_account_name=( + setting.storage_account_id.split("/")[-1] + if getattr(setting, "storage_account_id", None) + else None + ), logs=setting.logs, storage_account_id=setting.storage_account_id, ) ) except Exception as error: logger.error( - f"Subscription name: {subscription} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + f"Subscription id: {subscription} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) return diagnostics_settings diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled_test.py index 9be47576ac..93a3eacf04 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_cloudwatch_logging_enabled/cloudtrail_cloudwatch_logging_enabled_test.py @@ -89,7 +89,7 @@ class Test_cloudtrail_cloudwatch_logging_enabled: cloudtrail_cloudwatch_logging_enabled, ) - for trail in service_client.trails: + for trail in service_client.trails.values(): if trail.name == trail_name_us: trail.latest_cloudwatch_delivery_time = datetime.now().replace( tzinfo=timezone.utc @@ -178,7 +178,7 @@ class Test_cloudtrail_cloudwatch_logging_enabled: cloudtrail_cloudwatch_logging_enabled, ) - for trail in service_client.trails: + for trail in service_client.trails.values(): if trail.name == trail_name_us: trail.latest_cloudwatch_delivery_time = datetime.now().replace( tzinfo=timezone.utc @@ -194,8 +194,8 @@ class Test_cloudtrail_cloudwatch_logging_enabled: check = cloudtrail_cloudwatch_logging_enabled() result = check.execute() - # len of result should be 3 -> (1 multiregion entry per region + 1 entry because of single region trail) - assert len(result) == 3 + # len of result should be 2 -> (1 per trail) + assert len(result) == 2 for report in result: if report.resource_id == trail_name_us: assert report.resource_id == trail_name_us @@ -268,7 +268,7 @@ class Test_cloudtrail_cloudwatch_logging_enabled: cloudtrail_cloudwatch_logging_enabled, ) - for trail in service_client.trails: + for trail in service_client.trails.values(): if trail.name == trail_name_us: trail.latest_cloudwatch_delivery_time = datetime.now().replace( tzinfo=timezone.utc diff --git a/tests/providers/aws/services/cloudtrail/cloudtrail_service_test.py b/tests/providers/aws/services/cloudtrail/cloudtrail_service_test.py index 02305ba3c4..ed35eaf137 100644 --- a/tests/providers/aws/services/cloudtrail/cloudtrail_service_test.py +++ b/tests/providers/aws/services/cloudtrail/cloudtrail_service_test.py @@ -88,7 +88,7 @@ class Test_Cloudtrail_Service: ) cloudtrail = Cloudtrail(aws_provider) assert len(cloudtrail.trails) == 2 - for trail in cloudtrail.trails: + for trail in cloudtrail.trails.values(): if trail.name: assert trail.name == trail_name_us or trail.name == trail_name_eu assert not trail.is_multiregion @@ -145,7 +145,7 @@ class Test_Cloudtrail_Service: ) cloudtrail = Cloudtrail(aws_provider) assert len(cloudtrail.trails) == len(aws_provider.identity.audited_regions) - for trail in cloudtrail.trails: + for trail in cloudtrail.trails.values(): if trail.name: if trail.name == trail_name_us: assert not trail.is_multiregion @@ -189,7 +189,7 @@ class Test_Cloudtrail_Service: ) cloudtrail = Cloudtrail(aws_provider) assert len(cloudtrail.trails) == len(aws_provider.identity.audited_regions) - for trail in cloudtrail.trails: + for trail in cloudtrail.trails.values(): if trail.name: if trail.name == trail_name_us: assert not trail.is_multiregion @@ -237,7 +237,7 @@ class Test_Cloudtrail_Service: ) cloudtrail = Cloudtrail(aws_provider) assert len(cloudtrail.trails) == len(aws_provider.identity.audited_regions) - for trail in cloudtrail.trails: + for trail in cloudtrail.trails.values(): if trail.name: if trail.name == trail_name_us: assert not trail.is_multiregion diff --git a/tests/providers/azure/services/app/app_client_certificates_on/app_client_certificates_on_test.py b/tests/providers/azure/services/app/app_client_certificates_on/app_client_certificates_on_test.py index 50bc47e7de..07601f2641 100644 --- a/tests/providers/azure/services/app/app_client_certificates_on/app_client_certificates_on_test.py +++ b/tests/providers/azure/services/app/app_client_certificates_on/app_client_certificates_on_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_client_certificates_on: def test_app_client_certificates_on(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Required", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_client_certificates_on: from prowler.providers.azure.services.app.app_client_certificates_on.app_client_certificates_on import ( app_client_certificates_on, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Required", + https_only=False, + identity=None, + ) + } + } check = app_client_certificates_on() result = check.execute() assert len(result) == 1 @@ -89,18 +89,6 @@ class Test_app_client_certificates_on: def test_app_client_certificates_off(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -112,7 +100,20 @@ class Test_app_client_certificates_on: from prowler.providers.azure.services.app.app_client_certificates_on.app_client_certificates_on import ( app_client_certificates_on, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_client_certificates_on() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_ensure_auth_is_set_up/app_ensure_auth_is_set_up_test.py b/tests/providers/azure/services/app/app_ensure_auth_is_set_up/app_ensure_auth_is_set_up_test.py index 0b718148c2..5cb3ef44d3 100644 --- a/tests/providers/azure/services/app/app_ensure_auth_is_set_up/app_ensure_auth_is_set_up_test.py +++ b/tests/providers/azure/services/app/app_ensure_auth_is_set_up/app_ensure_auth_is_set_up_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_ensure_auth_is_set_up: def test_app_auth_enabled(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_ensure_auth_is_set_up: from prowler.providers.azure.services.app.app_ensure_auth_is_set_up.app_ensure_auth_is_set_up import ( app_ensure_auth_is_set_up, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_auth_is_set_up() result = check.execute() assert len(result) == 1 @@ -89,18 +89,6 @@ class Test_app_ensure_auth_is_set_up: def test_app_auth_disabled(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=False, - configurations=mock.MagicMock(), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -112,7 +100,20 @@ class Test_app_ensure_auth_is_set_up: from prowler.providers.azure.services.app.app_ensure_auth_is_set_up.app_ensure_auth_is_set_up import ( app_ensure_auth_is_set_up, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=False, + configurations=mock.MagicMock(), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_auth_is_set_up() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_ensure_http_is_redirected_to_https/app_ensure_http_is_redirected_to_https_test.py b/tests/providers/azure/services/app/app_ensure_http_is_redirected_to_https/app_ensure_http_is_redirected_to_https_test.py index fa982cc648..f781a74b01 100644 --- a/tests/providers/azure/services/app/app_ensure_http_is_redirected_to_https/app_ensure_http_is_redirected_to_https_test.py +++ b/tests/providers/azure/services/app/app_ensure_http_is_redirected_to_https/app_ensure_http_is_redirected_to_https_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_ensure_http_is_redirected_to_https: def test_app_http_to_https(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_ensure_http_is_redirected_to_https: from prowler.providers.azure.services.app.app_ensure_http_is_redirected_to_https.app_ensure_http_is_redirected_to_https import ( app_ensure_http_is_redirected_to_https, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_http_is_redirected_to_https() result = check.execute() assert len(result) == 1 @@ -89,18 +89,6 @@ class Test_app_ensure_http_is_redirected_to_https: def test_app_http_to_https_enabled(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(), - client_cert_mode="Ignore", - https_only=True, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -112,7 +100,20 @@ class Test_app_ensure_http_is_redirected_to_https: from prowler.providers.azure.services.app.app_ensure_http_is_redirected_to_https.app_ensure_http_is_redirected_to_https import ( app_ensure_http_is_redirected_to_https, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(), + client_cert_mode="Ignore", + https_only=True, + identity=None, + ) + } + } check = app_ensure_http_is_redirected_to_https() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_ensure_java_version_is_latest/app_ensure_java_version_is_latest_test.py b/tests/providers/azure/services/app/app_ensure_java_version_is_latest/app_ensure_java_version_is_latest_test.py index cb8bd5cd81..b6f4123977 100644 --- a/tests/providers/azure/services/app/app_ensure_java_version_is_latest/app_ensure_java_version_is_latest_test.py +++ b/tests/providers/azure/services/app/app_ensure_java_version_is_latest/app_ensure_java_version_is_latest_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_ensure_java_version_is_latest: def test_app_configurations_none(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_ensure_java_version_is_latest: from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import ( app_ensure_java_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_java_version_is_latest() result = check.execute() assert len(result) == 0 @@ -81,20 +81,6 @@ class Test_app_ensure_java_version_is_latest: def test_app_linux_java_version_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock( - linux_fx_version="Tomcat|9.0-java17", java_version=None - ), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"java_latest_version": "17"} @@ -108,7 +94,22 @@ class Test_app_ensure_java_version_is_latest: from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import ( app_ensure_java_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock( + linux_fx_version="Tomcat|9.0-java17", java_version=None + ), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_java_version_is_latest() result = check.execute() assert len(result) == 1 @@ -124,20 +125,6 @@ class Test_app_ensure_java_version_is_latest: def test_app_linux_java_version_not_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock( - linux_fx_version="Tomcat|9.0-java11", java_version=None - ), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"java_latest_version": "17"} @@ -151,7 +138,22 @@ class Test_app_ensure_java_version_is_latest: from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import ( app_ensure_java_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock( + linux_fx_version="Tomcat|9.0-java11", java_version=None + ), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_java_version_is_latest() result = check.execute() assert len(result) == 1 @@ -167,20 +169,6 @@ class Test_app_ensure_java_version_is_latest: def test_app_windows_java_version_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock( - linux_fx_version="", java_version="17" - ), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"java_latest_version": "17"} @@ -194,7 +182,22 @@ class Test_app_ensure_java_version_is_latest: from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import ( app_ensure_java_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock( + linux_fx_version="", java_version="17" + ), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_java_version_is_latest() result = check.execute() assert len(result) == 1 @@ -210,20 +213,6 @@ class Test_app_ensure_java_version_is_latest: def test_app_windows_java_version_not_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock( - linux_fx_version="", java_version="11" - ), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"java_latest_version": "17"} @@ -237,7 +226,22 @@ class Test_app_ensure_java_version_is_latest: from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import ( app_ensure_java_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock( + linux_fx_version="", java_version="11" + ), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_java_version_is_latest() result = check.execute() assert len(result) == 1 @@ -253,20 +257,6 @@ class Test_app_ensure_java_version_is_latest: def test_app_linux_php_version_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock( - linux_fx_version="php|8.0", java_version=None - ), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"java_latest_version": "17"} @@ -280,7 +270,22 @@ class Test_app_ensure_java_version_is_latest: from prowler.providers.azure.services.app.app_ensure_java_version_is_latest.app_ensure_java_version_is_latest import ( app_ensure_java_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock( + linux_fx_version="php|8.0", java_version=None + ), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_java_version_is_latest() result = check.execute() assert len(result) == 0 diff --git a/tests/providers/azure/services/app/app_ensure_php_version_is_latest/app_ensure_php_version_is_latest_test.py b/tests/providers/azure/services/app/app_ensure_php_version_is_latest/app_ensure_php_version_is_latest_test.py index cda0362919..c3855bd3f3 100644 --- a/tests/providers/azure/services/app/app_ensure_php_version_is_latest/app_ensure_php_version_is_latest_test.py +++ b/tests/providers/azure/services/app/app_ensure_php_version_is_latest/app_ensure_php_version_is_latest_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_ensure_php_version_is_latest: def test_app_configurations_none(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_ensure_php_version_is_latest: from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import ( app_ensure_php_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_php_version_is_latest() result = check.execute() assert len(result) == 0 @@ -81,18 +81,6 @@ class Test_app_ensure_php_version_is_latest: def test_app_php_version_not_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(linux_fx_version="php|8.0"), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"php_latest_version": "8.2"} @@ -106,7 +94,20 @@ class Test_app_ensure_php_version_is_latest: from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import ( app_ensure_php_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(linux_fx_version="php|8.0"), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_php_version_is_latest() result = check.execute() assert len(result) == 1 @@ -122,18 +123,6 @@ class Test_app_ensure_php_version_is_latest: def test_app_php_version_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(linux_fx_version="php|8.2"), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"php_latest_version": "8.2"} @@ -147,7 +136,20 @@ class Test_app_ensure_php_version_is_latest: from prowler.providers.azure.services.app.app_ensure_php_version_is_latest.app_ensure_php_version_is_latest import ( app_ensure_php_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(linux_fx_version="php|8.2"), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_php_version_is_latest() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_ensure_python_version_is_latest/app_ensure_python_version_is_latest_test.py b/tests/providers/azure/services/app/app_ensure_python_version_is_latest/app_ensure_python_version_is_latest_test.py index 4310a26115..89e98bf1ab 100644 --- a/tests/providers/azure/services/app/app_ensure_python_version_is_latest/app_ensure_python_version_is_latest_test.py +++ b/tests/providers/azure/services/app/app_ensure_python_version_is_latest/app_ensure_python_version_is_latest_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_ensure_python_version_is_latest: def test_app_configurations_none(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_ensure_python_version_is_latest: from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import ( app_ensure_python_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_python_version_is_latest() result = check.execute() assert len(result) == 0 @@ -81,18 +81,6 @@ class Test_app_ensure_python_version_is_latest: def test_app_python_version_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(linux_fx_version="python|3.12"), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"python_latest_version": "3.12"} @@ -106,7 +94,20 @@ class Test_app_ensure_python_version_is_latest: from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import ( app_ensure_python_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(linux_fx_version="python|3.12"), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_python_version_is_latest() result = check.execute() assert len(result) == 1 @@ -122,18 +123,6 @@ class Test_app_ensure_python_version_is_latest: def test_app_python_version_not_latest(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(linux_fx_version="python|3.10"), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } app_client.audit_config = {"python_latest_version": "3.12"} @@ -147,7 +136,20 @@ class Test_app_ensure_python_version_is_latest: from prowler.providers.azure.services.app.app_ensure_python_version_is_latest.app_ensure_python_version_is_latest import ( app_ensure_python_version_is_latest, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(linux_fx_version="python|3.10"), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_python_version_is_latest() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_ensure_using_http20/app_ensure_using_http20_test.py b/tests/providers/azure/services/app/app_ensure_using_http20/app_ensure_using_http20_test.py index f0023b1f18..15276daaa5 100644 --- a/tests/providers/azure/services/app/app_ensure_using_http20/app_ensure_using_http20_test.py +++ b/tests/providers/azure/services/app/app_ensure_using_http20/app_ensure_using_http20_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_ensure_using_http20: def test_app_configurations_none(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_ensure_using_http20: from prowler.providers.azure.services.app.app_ensure_using_http20.app_ensure_using_http20 import ( app_ensure_using_http20, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_using_http20() result = check.execute() assert len(result) == 1 @@ -89,18 +89,6 @@ class Test_app_ensure_using_http20: def test_app_http20_enabled(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(http20_enabled=True), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -112,7 +100,20 @@ class Test_app_ensure_using_http20: from prowler.providers.azure.services.app.app_ensure_using_http20.app_ensure_using_http20 import ( app_ensure_using_http20, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(http20_enabled=True), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_using_http20() result = check.execute() assert len(result) == 1 @@ -128,18 +129,6 @@ class Test_app_ensure_using_http20: def test_app_http20_not_enabled(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(http20_enabled=False), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -151,7 +140,20 @@ class Test_app_ensure_using_http20: from prowler.providers.azure.services.app.app_ensure_using_http20.app_ensure_using_http20 import ( app_ensure_using_http20, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(http20_enabled=False), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ensure_using_http20() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_ftp_deployment_disabled/app_ftp_deployment_disabled_test.py b/tests/providers/azure/services/app/app_ftp_deployment_disabled/app_ftp_deployment_disabled_test.py index f74d2bfec4..4670b407f2 100644 --- a/tests/providers/azure/services/app/app_ftp_deployment_disabled/app_ftp_deployment_disabled_test.py +++ b/tests/providers/azure/services/app/app_ftp_deployment_disabled/app_ftp_deployment_disabled_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_ftp_deployment_disabled: def test_app_configurations_none(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_ftp_deployment_disabled: from prowler.providers.azure.services.app.app_ftp_deployment_disabled.app_ftp_deployment_disabled import ( app_ftp_deployment_disabled, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ftp_deployment_disabled() result = check.execute() assert len(result) == 1 @@ -89,18 +89,6 @@ class Test_app_ftp_deployment_disabled: def test_app_ftp_deployment_disabled(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(ftps_state="AllAllowed"), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -112,7 +100,20 @@ class Test_app_ftp_deployment_disabled: from prowler.providers.azure.services.app.app_ftp_deployment_disabled.app_ftp_deployment_disabled import ( app_ftp_deployment_disabled, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(ftps_state="AllAllowed"), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ftp_deployment_disabled() result = check.execute() assert len(result) == 1 @@ -128,18 +129,6 @@ class Test_app_ftp_deployment_disabled: def test_app_ftp_deploy_enabled(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(ftps_state="Disabled"), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -151,7 +140,20 @@ class Test_app_ftp_deployment_disabled: from prowler.providers.azure.services.app.app_ftp_deployment_disabled.app_ftp_deployment_disabled import ( app_ftp_deployment_disabled, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(ftps_state="Disabled"), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_ftp_deployment_disabled() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled_test.py b/tests/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled_test.py new file mode 100644 index 0000000000..e008fe35ac --- /dev/null +++ b/tests/providers/azure/services/app/app_http_logs_enabled/app_http_logs_enabled_test.py @@ -0,0 +1,207 @@ +from unittest import mock + +from tests.providers.azure.azure_fixtures import ( + AZURE_SUBSCRIPTION_ID, + set_mocked_azure_provider, +) + + +class Test_app_http_logs_enabled: + + def test_app_http_logs_enabled_no_subscriptions(self): + app_client = mock.MagicMock + app_client.apps = {} + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_azure_provider(), + ), mock.patch( + "prowler.providers.azure.services.app.app_http_logs_enabled.app_http_logs_enabled.app_client", + new=app_client, + ): + + from prowler.providers.azure.services.app.app_http_logs_enabled.app_http_logs_enabled import ( + app_http_logs_enabled, + ) + + check = app_http_logs_enabled() + result = check.execute() + assert len(result) == 0 + + def test_app_subscriptions_empty(self): + app_client = mock.MagicMock + app_client.apps = {AZURE_SUBSCRIPTION_ID: {}} + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_azure_provider(), + ), mock.patch( + "prowler.providers.azure.services.app.app_register_with_identity.app_register_with_identity.app_client", + new=app_client, + ): + from prowler.providers.azure.services.app.app_register_with_identity.app_register_with_identity import ( + app_register_with_identity, + ) + + check = app_register_with_identity() + result = check.execute() + assert len(result) == 0 + + def test_no_diagnostics_settings(self): + app_client = mock.MagicMock() + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_azure_provider(), + ), mock.patch( + "prowler.providers.azure.services.app.app_http_logs_enabled.app_http_logs_enabled.app_client", + new=app_client, + ): + from prowler.providers.azure.services.app.app_http_logs_enabled.app_http_logs_enabled import ( + app_http_logs_enabled, + ) + from prowler.providers.azure.services.app.app_service import WebApp + + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app1": WebApp( + resource_id="resource_id", + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + kind="webapps", + ) + } + } + + check = app_http_logs_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].resource_name == "app1" + assert result[0].resource_id == "resource_id" + assert ( + result[0].status_extended + == f"App app1 does not have a diagnostic setting in subscription {AZURE_SUBSCRIPTION_ID}." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + + def test_diagnostic_setting_configured(self): + app_client = mock.MagicMock + + with mock.patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_azure_provider(), + ), mock.patch( + "prowler.providers.azure.services.app.app_http_logs_enabled.app_http_logs_enabled.app_client", + new=app_client, + ): + from prowler.providers.azure.services.app.app_http_logs_enabled.app_http_logs_enabled import ( + app_http_logs_enabled, + ) + from prowler.providers.azure.services.app.app_service import WebApp + from prowler.providers.azure.services.monitor.monitor_service import ( + DiagnosticSetting, + ) + + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id="resource_id1", + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + kind="functionapp", + identity=mock.MagicMock, + monitor_diagnostic_settings=[ + DiagnosticSetting( + id="id1/id1", + logs=[ + mock.MagicMock( + category="AppServiceHTTPLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServiceConsoleLogs", + enabled=False, + ), + mock.MagicMock( + category="AppServiceAppLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServiceAuditLogs", + enabled=False, + ), + mock.MagicMock( + category="AppServiceIPSecAuditLogs", + enabled=False, + ), + mock.MagicMock( + category="AppServicePlatformLogs", + enabled=False, + ), + ], + storage_account_name="storage_account_name1", + storage_account_id="storage_account_id1", + name="name_diagnostic_setting1", + ), + ], + ), + "app_id-2": WebApp( + resource_id="resource_id2", + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + kind="WebApp", + identity=mock.MagicMock, + monitor_diagnostic_settings=[ + DiagnosticSetting( + id="id2/id2", + logs=[ + mock.MagicMock( + category="AppServiceHTTPLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServiceConsoleLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServiceAppLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServiceAuditLogs", + enabled=False, + ), + mock.MagicMock( + category="AppServiceIPSecAuditLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServicePlatformLogs", + enabled=False, + ), + ], + storage_account_name="storage_account_name2", + storage_account_id="storage_account_id2", + name="name_diagnostic_setting2", + ), + ], + ), + } + } + check = app_http_logs_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == "app_id-2" + assert result[0].resource_id == "resource_id2" + assert ( + result[0].status_extended + == f"App app_id-2 has HTTP Logs enabled in diagnostic setting name_diagnostic_setting2 in subscription {AZURE_SUBSCRIPTION_ID}" + ) diff --git a/tests/providers/azure/services/app/app_minimum_tls_version_12/app_minimum_tls_version_12_test.py b/tests/providers/azure/services/app/app_minimum_tls_version_12/app_minimum_tls_version_12_test.py index 82be606224..143d70ec4f 100644 --- a/tests/providers/azure/services/app/app_minimum_tls_version_12/app_minimum_tls_version_12_test.py +++ b/tests/providers/azure/services/app/app_minimum_tls_version_12/app_minimum_tls_version_12_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_minimum_tls_version_12: def test_app_none_configurations(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_minimum_tls_version_12: from prowler.providers.azure.services.app.app_minimum_tls_version_12.app_minimum_tls_version_12 import ( app_minimum_tls_version_12, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_minimum_tls_version_12() result = check.execute() assert len(result) == 1 @@ -89,18 +89,6 @@ class Test_app_minimum_tls_version_12: def test_app_min_tls_version_12(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=mock.MagicMock(min_tls_version="1.2"), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -112,7 +100,20 @@ class Test_app_minimum_tls_version_12: from prowler.providers.azure.services.app.app_minimum_tls_version_12.app_minimum_tls_version_12 import ( app_minimum_tls_version_12, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=mock.MagicMock(min_tls_version="1.2"), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_minimum_tls_version_12() result = check.execute() assert len(result) == 1 @@ -128,18 +129,6 @@ class Test_app_minimum_tls_version_12: def test_app_min_tls_version_10(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=False, - configurations=mock.MagicMock(min_tls_version="1.0"), - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -151,7 +140,20 @@ class Test_app_minimum_tls_version_12: from prowler.providers.azure.services.app.app_minimum_tls_version_12.app_minimum_tls_version_12 import ( app_minimum_tls_version_12, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=False, + configurations=mock.MagicMock(min_tls_version="1.0"), + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_minimum_tls_version_12() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_register_with_identity/app_register_with_identity_test.py b/tests/providers/azure/services/app/app_register_with_identity/app_register_with_identity_test.py index e25141022f..985699a340 100644 --- a/tests/providers/azure/services/app/app_register_with_identity/app_register_with_identity_test.py +++ b/tests/providers/azure/services/app/app_register_with_identity/app_register_with_identity_test.py @@ -1,7 +1,6 @@ from unittest import mock from uuid import uuid4 -from prowler.providers.azure.services.app.app_service import WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, @@ -50,18 +49,6 @@ class Test_app_register_with_identity: def test_app_none_configurations(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=None, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -73,7 +60,20 @@ class Test_app_register_with_identity: from prowler.providers.azure.services.app.app_register_with_identity.app_register_with_identity import ( app_register_with_identity, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=None, + ) + } + } check = app_register_with_identity() result = check.execute() assert len(result) == 1 @@ -89,18 +89,6 @@ class Test_app_register_with_identity: def test_app_identity(self): resource_id = f"/subscriptions/{uuid4()}" app_client = mock.MagicMock - app_client.apps = { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id=resource_id, - auth_enabled=True, - configurations=None, - client_cert_mode="Ignore", - https_only=False, - identity=mock.MagicMock, - ) - } - } with mock.patch( "prowler.providers.common.common.get_global_provider", @@ -112,7 +100,20 @@ class Test_app_register_with_identity: from prowler.providers.azure.services.app.app_register_with_identity.app_register_with_identity import ( app_register_with_identity, ) + from prowler.providers.azure.services.app.app_service import WebApp + app_client.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id=resource_id, + auth_enabled=True, + configurations=None, + client_cert_mode="Ignore", + https_only=False, + identity=mock.MagicMock, + ) + } + } check = app_register_with_identity() result = check.execute() assert len(result) == 1 diff --git a/tests/providers/azure/services/app/app_service_test.py b/tests/providers/azure/services/app/app_service_test.py index 2c563686fe..614ca39787 100644 --- a/tests/providers/azure/services/app/app_service_test.py +++ b/tests/providers/azure/services/app/app_service_test.py @@ -1,47 +1,132 @@ -from unittest.mock import patch +from unittest import mock +from unittest.mock import MagicMock, patch from azure.mgmt.web.models import ManagedServiceIdentity, SiteConfigResource -from prowler.providers.azure.services.app.app_service import App, WebApp from tests.providers.azure.azure_fixtures import ( AZURE_SUBSCRIPTION_ID, set_mocked_azure_provider, ) - -def mock_app_get_apps(_): - return { - AZURE_SUBSCRIPTION_ID: { - "app_id-1": WebApp( - resource_id="/subscriptions/resource_id", - configurations=SiteConfigResource(), - identity=ManagedServiceIdentity(type="SystemAssigned"), - auth_enabled=True, - client_cert_mode="Required", - https_only=True, - ) - } - } +# TODO: we have to fix this test not to use MagicMock but set the App service while mocking the import ot the Monitor client +# def mock_app_get_apps(_): +# return { +# AZURE_SUBSCRIPTION_ID: { +# "app_id-1": WebApp( +# resource_id="/subscriptions/resource_id", +# configurations=SiteConfigResource(), +# identity=ManagedServiceIdentity(type="SystemAssigned"), +# auth_enabled=True, +# client_cert_mode="Required", +# https_only=True, +# monitor_diagnostic_settings=[ +# DiagnosticSetting( +# id="id2/id2", +# logs=[ +# mock.MagicMock( +# category="AppServiceHTTPLogs", +# enabled=False, +# ), +# mock.MagicMock( +# category="AppServiceConsoleLogs", +# enabled=True, +# ), +# mock.MagicMock( +# category="AppServiceAppLogs", +# enabled=True, +# ), +# mock.MagicMock( +# category="AppServiceAuditLogs", +# enabled=False, +# ), +# mock.MagicMock( +# category="AppServiceIPSecAuditLogs", +# enabled=True, +# ), +# mock.MagicMock( +# category="AppServicePlatformLogs", +# enabled=False, +# ), +# ], +# storage_account_name="storage_account_name2", +# storage_account_id="storage_account_id2", +# name="name_diagnostic_setting2", +# ), +# ], +# ) +# } +# } -@patch( - "prowler.providers.azure.services.app.app_service.App.__get_apps__", - new=mock_app_get_apps, -) +# @patch( +# "prowler.providers.azure.services.app.app_service.App.__get_apps__", +# new=mock_app_get_apps, +# ) class Test_App_Service: - def test__get_client__(self): - app_service = App(set_mocked_azure_provider()) - assert ( - app_service.clients[AZURE_SUBSCRIPTION_ID].__class__.__name__ - == "WebSiteManagementClient" - ) + def test_app_service_(self): + with patch( + "prowler.providers.common.common.get_global_provider", + return_value=set_mocked_azure_provider(), + ), patch( + "prowler.providers.azure.services.monitor.monitor_service.Monitor", + new=MagicMock(), + ): + from prowler.providers.azure.services.app.app_service import WebApp + from prowler.providers.azure.services.monitor.monitor_service import ( + DiagnosticSetting, + ) - def test__get_subscriptions__(self): - app_service = App(set_mocked_azure_provider()) - assert app_service.subscriptions.__class__.__name__ == "dict" - - def test__get_apps__(self): - app_service = App(set_mocked_azure_provider()) + app_service = MagicMock() + app_service.apps = { + AZURE_SUBSCRIPTION_ID: { + "app_id-1": WebApp( + resource_id="/subscriptions/resource_id", + configurations=SiteConfigResource(), + identity=ManagedServiceIdentity(type="SystemAssigned"), + auth_enabled=True, + client_cert_mode="Required", + https_only=True, + monitor_diagnostic_settings=[ + DiagnosticSetting( + id="id2/id2", + logs=[ + mock.MagicMock( + category="AppServiceHTTPLogs", + enabled=False, + ), + mock.MagicMock( + category="AppServiceConsoleLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServiceAppLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServiceAuditLogs", + enabled=False, + ), + mock.MagicMock( + category="AppServiceIPSecAuditLogs", + enabled=True, + ), + mock.MagicMock( + category="AppServicePlatformLogs", + enabled=False, + ), + ], + storage_account_name="storage_account_name2", + storage_account_id="storage_account_id2", + name="name_diagnostic_setting2", + ), + ], + ) + } + } + # assert ( + # app_service.clients[AZURE_SUBSCRIPTION_ID][0].__class__.__name__ + # == "WebSiteManagementClient" + # ) assert len(app_service.apps) == 1 assert ( app_service.apps[AZURE_SUBSCRIPTION_ID]["app_id-1"].resource_id @@ -63,17 +148,34 @@ class Test_App_Service: ].configurations.__class__.__name__ == "SiteConfigResource" ) - - def test__get_client_cert_mode__(self): - app_service = App(set_mocked_azure_provider()) assert ( - app_service.__get_client_cert_mode__(False, "OptionalInteractiveUser") - == "Ignore" + app_service.apps[AZURE_SUBSCRIPTION_ID]["app_id-1"] + .monitor_diagnostic_settings[0] + .id + == "id2/id2" ) assert ( - app_service.__get_client_cert_mode__(True, "OptionalInteractiveUser") - == "Optional" + app_service.apps[AZURE_SUBSCRIPTION_ID]["app_id-1"] + .monitor_diagnostic_settings[0] + .logs[0] + .category + == "AppServiceHTTPLogs" + ) + assert ( + app_service.apps[AZURE_SUBSCRIPTION_ID]["app_id-1"] + .monitor_diagnostic_settings[0] + .storage_account_name + == "storage_account_name2" + ) + assert ( + app_service.apps[AZURE_SUBSCRIPTION_ID]["app_id-1"] + .monitor_diagnostic_settings[0] + .storage_account_id + == "storage_account_id2" + ) + assert ( + app_service.apps[AZURE_SUBSCRIPTION_ID]["app_id-1"] + .monitor_diagnostic_settings[0] + .name + == "name_diagnostic_setting2" ) - assert app_service.__get_client_cert_mode__(True, "Optional") == "Allow" - assert app_service.__get_client_cert_mode__(True, "Required") == "Required" - assert app_service.__get_client_cert_mode__(True, "Foo") == "Ignore"