Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(appsync): add new check appsync_field_level_logging_enabled #5602

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"Provider": "aws",
"CheckID": "appsync_field_level_logging_enabled",
"CheckTitle": "AWS AppSync should have field-level logging enabled",
"CheckType": [
"Software and Configuration Checks/AWS Security Best Practices"
],
"ServiceName": "appsync",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:appsync:{region}:{account-id}:apis/{api-id}",
"Severity": "medium",
"ResourceType": "AwsAppSyncGraphQLApi",
"Description": "This control checks whether an AWS AppSync API has field-level logging turned on. The control fails if the field resolver log level is set to None.",
"Risk": "Without field-level logging enabled, it's difficult to monitor, troubleshoot, and optimize GraphQL API queries effectively.",
"RelatedUrl": "https://docs.aws.amazon.com/config/latest/developerguide/appsync-logging-enabled.html",
"Remediation": {
"Code": {
"CLI": "aws appsync update-graphql-api --api-id <api-id> --log-config fieldLogLevel=<fieldLoggingLevel>",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/appsync-controls.html#appsync-2",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable field-level logging for your AWS AppSync API to monitor and troubleshoot GraphQL queries effectively.",
"Url": "https://docs.aws.amazon.com/appsync/latest/devguide/monitoring.html#setup-and-configuration"
}
},
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.appsync.appsync_client import appsync_client


class appsync_field_level_logging_enabled(Check):
def execute(self):
findings = []
for api in appsync_client.graphqlapis.values():
report = Check_Report_AWS(self.metadata())
report.region = api.region
report.resource_id = api.id
report.resource_arn = api.arn
report.resource_tags = api.tags
report.status = "PASS"
report.status_extended = (
f"AppSync API {api.name} has field log level enabled."
)
if api.field_log_level != "ALL" and api.field_log_level != "ERROR":
report.status = "FAIL"
report.status_extended = (
f"AppSync API {api.name} does not have field log level enabled."
)
findings.append(report)

return findings
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from unittest import mock

import botocore
from boto3 import client
from moto import mock_aws

from prowler.providers.aws.services.appsync.appsync_service import AppSync
from tests.providers.aws.utils import (
AWS_ACCOUNT_NUMBER,
AWS_REGION_US_EAST_1,
set_mocked_aws_provider,
)

orig = botocore.client.BaseClient._make_api_call


def mock_make_api_call(self, operation_name, kwarg):
if operation_name == "ListGraphqlApis":
return {
"graphqlApis": [
{
"name": "test-log-level",
"apiId": "idididid",
"apiType": "MERGED",
"arn": f"arn:aws:appsync:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:graphqlapi/test-log-level",
"authenticationType": "API_KEY",
"logConfig": {"fieldLogLevel": "ALL"},
"region": AWS_REGION_US_EAST_1,
"tags": {"test": "test", "test2": "test2"},
},
]
}
return orig(self, operation_name, kwarg)


def mock_make_api_call_v2(self, operation_name, kwarg):
if operation_name == "ListGraphqlApis":
return {
"graphqlApis": [
{
"name": "test-none-log-level",
"apiId": "idididid",
"apiType": "GRAPHQL",
"arn": f"arn:aws:appsync:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:graphqlapi/test-none-log-level",
"authenticationType": "AWS_IAM",
"logConfig": {"fieldLogLevel": "NONE"},
"region": AWS_REGION_US_EAST_1,
"tags": {"test": "test", "test2": "test2"},
},
]
}
return orig(self, operation_name, kwarg)


class Test_appsync_field_level_logging_enabled:
@mock_aws
def test_no_apis(self):
client("appsync", region_name=AWS_REGION_US_EAST_1)

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.appsync.appsync_field_level_logging_enabled.appsync_field_level_logging_enabled.appsync_client",
new=AppSync(aws_provider),
):
# Test Check
from prowler.providers.aws.services.appsync.appsync_field_level_logging_enabled.appsync_field_level_logging_enabled import (
appsync_field_level_logging_enabled,
)

check = appsync_field_level_logging_enabled()
result = check.execute()

assert len(result) == 0

@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
def test_graphql_no_api_key(self):

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.appsync.appsync_field_level_logging_enabled.appsync_field_level_logging_enabled.appsync_client",
new=AppSync(aws_provider),
):
# Test Check
from prowler.providers.aws.services.appsync.appsync_field_level_logging_enabled.appsync_field_level_logging_enabled import (
appsync_field_level_logging_enabled,
)

check = appsync_field_level_logging_enabled()
result = check.execute()

assert len(result) == 1
assert (
result[0].resource_arn
== f"arn:aws:appsync:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:graphqlapi/test-log-level"
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == "idididid"
assert result[0].status == "PASS"
assert (
result[0].status_extended
== "AppSync API test-log-level has field log level enabled."
)
assert result[0].resource_tags == [{"test": "test", "test2": "test2"}]

@mock.patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call_v2)
def test_graphql_api_key(self):

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.appsync.appsync_field_level_logging_enabled.appsync_field_level_logging_enabled.appsync_client",
new=AppSync(aws_provider),
):
# Test Check
from prowler.providers.aws.services.appsync.appsync_field_level_logging_enabled.appsync_field_level_logging_enabled import (
appsync_field_level_logging_enabled,
)

check = appsync_field_level_logging_enabled()
result = check.execute()

assert len(result) == 1
assert (
result[0].resource_arn
== f"arn:aws:appsync:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:graphqlapi/test-none-log-level"
)
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_id == "idididid"
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== "AppSync API test-none-log-level does not have field log level enabled."
)
assert result[0].resource_tags == [{"test": "test", "test2": "test2"}]
Loading