feat(codebuild): add new check codebuild_project_logging_enabled (#5365)

Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
This commit is contained in:
Rubén De la Torre Vico
2024-10-15 13:42:45 +02:00
committed by GitHub
parent 0449d6372c
commit aac6038565
6 changed files with 379 additions and 1 deletions

View File

@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "codebuild_project_logging_enabled",
"CheckTitle": "Ensure that CodeBuild projects have S3 or CloudWatch logging enabled",
"CheckType": [],
"ServiceName": "codebuild",
"SubServiceName": "",
"ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id",
"Severity": "medium",
"ResourceType": "AwsCodeBuildProject",
"Description": "Ensure that CodeBuild projects have S3 or CloudWatch logging enabled.",
"Risk": "Without logging, tracking and investigating security incidents in CodeBuild projects becomes challenging, reducing confidence in threat detections.",
"RelatedUrl": "https://docs.aws.amazon.com/codebuild/latest/userguide/change-project.html#change-project-console-logs",
"Remediation": {
"Code": {
"CLI": "aws codebuild update-project --name <project-name> --logs-config \"cloudWatchLogs={status=ENABLED},s3Logs={status=ENABLED\"}",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/codebuild-controls.html#codebuild-4",
"Terraform": ""
},
"Recommendation": {
"Text": "Enable logging for CodeBuild projects to capture build events and logs for future analysis and incident response.",
"Url": "https://docs.aws.amazon.com/codebuild/latest/userguide/change-project.html#change-project-console-logs"
}
},
"Categories": [
"logging"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}

View File

@@ -0,0 +1,30 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.codebuild.codebuild_client import codebuild_client
class codebuild_project_logging_enabled(Check):
def execute(self):
findings = []
for project in codebuild_client.projects.values():
report = Check_Report_AWS(self.metadata())
report.resource_id = project.name
report.resource_arn = project.arn
report.region = project.region
report.resource_tags = project.tags
report.status = "PASS"
if project.cloudwatch_logs.enabled and project.s3_logs.enabled:
report.status_extended = f"CodeBuild project {project.name} has enabled CloudWartch logs in log group {project.cloudwatch_logs.group_name} and S3 logs in bucket {project.s3_logs.bucket_location}."
elif project.cloudwatch_logs.enabled:
report.status_extended = f"CodeBuild project {project.name} has CloudWatch logging enabled in log group {project.cloudwatch_logs.group_name}."
elif project.s3_logs.enabled:
report.status_extended = f"CodeBuild project {project.name} has S3 logging enabled in bucket {project.s3_logs.bucket_location}."
else:
report.status = "FAIL"
report.status_extended = (
f"CodeBuild project {project.name} does not have logging enabled."
)
findings.append(report)
return findings

View File

@@ -101,6 +101,18 @@ class Codebuild(AWSService):
bucket_location=s3_logs.get("location", ""),
encrypted=(not s3_logs.get("encryptionDisabled", False)),
)
cloudwatch_logs = project_info.get("logsConfig", {}).get(
"cloudWatchLogs", {}
)
project.cloudwatch_logs = CloudWatchLogs(
enabled=(
True
if cloudwatch_logs.get("status", "DISABLED") == "ENABLED"
else False
),
group_name=cloudwatch_logs.get("groupName", ""),
stream_name=cloudwatch_logs.get("streamName", ""),
)
project.tags = project_info.get("tags", [])
except Exception as error:
logger.error(
@@ -129,6 +141,12 @@ class s3Logs(BaseModel):
encrypted: bool
class CloudWatchLogs(BaseModel):
enabled: bool
group_name: str
stream_name: str
class Project(BaseModel):
name: str
arn: str
@@ -140,4 +158,5 @@ class Project(BaseModel):
secondary_sources: Optional[list[Source]] = []
environment_variables: Optional[List[EnvironmentVariable]]
s3_logs: Optional[s3Logs]
cloudwatch_logs: Optional[CloudWatchLogs]
tags: Optional[list]

View File

@@ -0,0 +1,281 @@
from unittest.mock import patch
from boto3 import client
from moto import mock_aws
from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider
class Test_codebuild_project_logging_enabled:
@mock_aws
def test_no_projects(self):
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
from prowler.providers.aws.services.codebuild.codebuild_service import Codebuild
with patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), patch(
"prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled.codebuild_client",
new=Codebuild(aws_provider),
):
from prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled import (
codebuild_project_logging_enabled,
)
check = codebuild_project_logging_enabled()
result = check.execute()
assert len(result) == 0
@mock_aws
def test_project_cloudwatch_logging_enabled(self):
codebuild_client = client("codebuild", region_name=AWS_REGION_EU_WEST_1)
project_name = "test-project-logging-enabled"
project_arn = codebuild_client.create_project(
name=project_name,
source={
"type": "S3",
"location": "test-bucket",
},
artifacts={
"type": "NO_ARTIFACTS",
},
environment={
"type": "LINUX_CONTAINER",
"image": "aws/codebuild/standard:4.0",
"computeType": "BUILD_GENERAL1_SMALL",
"environmentVariables": [],
},
serviceRole="arn:aws:iam::123456789012:role/service-role/codebuild-role",
logsConfig={
"cloudWatchLogs": {
"status": "ENABLED",
"groupName": "cw-test-group",
}
},
tags=[
{"key": "Name", "value": "test"},
],
)["project"]["arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
from prowler.providers.aws.services.codebuild.codebuild_service import Codebuild
with patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), patch(
"prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled.codebuild_client",
new=Codebuild(aws_provider),
):
from prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled import (
codebuild_project_logging_enabled,
)
check = codebuild_project_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"CodeBuild project {project_name} has CloudWatch logging enabled in log group cw-test-group."
)
assert result[0].resource_id == project_name
assert result[0].resource_arn == project_arn
assert result[0].resource_tags == [{"key": "Name", "value": "test"}]
assert result[0].region == AWS_REGION_EU_WEST_1
@mock_aws
def test_project_s3_logging_enabled(self):
codebuild_client = client("codebuild", region_name=AWS_REGION_EU_WEST_1)
project_name = "test-project-logging-enabled"
project_arn = codebuild_client.create_project(
name=project_name,
source={
"type": "S3",
"location": "test-bucket",
},
artifacts={
"type": "NO_ARTIFACTS",
},
environment={
"type": "LINUX_CONTAINER",
"image": "aws/codebuild/standard:4.0",
"computeType": "BUILD_GENERAL1_SMALL",
"environmentVariables": [],
},
serviceRole="arn:aws:iam::123456789012:role/service-role/codebuild-role",
logsConfig={
"s3Logs": {
"status": "ENABLED",
"location": "s3://test-bucket/logs",
}
},
tags=[
{"key": "Name", "value": "test"},
],
)["project"]["arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
from prowler.providers.aws.services.codebuild.codebuild_service import Codebuild
with patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), patch(
"prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled.codebuild_client",
new=Codebuild(aws_provider),
):
from prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled import (
codebuild_project_logging_enabled,
)
check = codebuild_project_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"CodeBuild project {project_name} has S3 logging enabled in bucket s3://test-bucket/logs."
)
assert result[0].resource_id == project_name
assert result[0].resource_arn == project_arn
assert result[0].resource_tags == [{"key": "Name", "value": "test"}]
assert result[0].region == AWS_REGION_EU_WEST_1
@mock_aws
def test_project_both_logging_enabled(self):
codebuild_client = client("codebuild", region_name=AWS_REGION_EU_WEST_1)
project_name = "test-project-logging-enabled"
project_arn = codebuild_client.create_project(
name=project_name,
source={
"type": "S3",
"location": "test-bucket",
},
artifacts={
"type": "NO_ARTIFACTS",
},
environment={
"type": "LINUX_CONTAINER",
"image": "aws/codebuild/standard:4.0",
"computeType": "BUILD_GENERAL1_SMALL",
"environmentVariables": [],
},
serviceRole="arn:aws:iam::123456789012:role/service-role/codebuild-role",
logsConfig={
"cloudWatchLogs": {
"status": "ENABLED",
"groupName": "cw-test-group",
},
"s3Logs": {
"status": "ENABLED",
"location": "s3://test-bucket/logs",
},
},
tags=[
{"key": "Name", "value": "test"},
],
)["project"]["arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
from prowler.providers.aws.services.codebuild.codebuild_service import Codebuild
with patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), patch(
"prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled.codebuild_client",
new=Codebuild(aws_provider),
):
from prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled import (
codebuild_project_logging_enabled,
)
check = codebuild_project_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"CodeBuild project {project_name} has enabled CloudWartch logs in log group cw-test-group and S3 logs in bucket s3://test-bucket/logs."
)
assert result[0].resource_id == project_name
assert result[0].resource_arn == project_arn
assert result[0].resource_tags == [{"key": "Name", "value": "test"}]
assert result[0].region == AWS_REGION_EU_WEST_1
@mock_aws
def test_project_logging_disabled(self):
codebuild_client = client("codebuild", region_name=AWS_REGION_EU_WEST_1)
project_name = "test-project-logging-disabled"
project_arn = codebuild_client.create_project(
name=project_name,
source={
"type": "S3",
"location": "test-bucket",
},
artifacts={
"type": "NO_ARTIFACTS",
},
environment={
"type": "LINUX_CONTAINER",
"image": "aws/codebuild/standard:4.0",
"computeType": "BUILD_GENERAL1_SMALL",
"environmentVariables": [],
},
serviceRole="arn:aws:iam::123456789012:role/service-role/codebuild-role",
logsConfig={
"cloudWatchLogs": {
"status": "DISABLED",
}
},
tags=[
{"key": "Name", "value": "test"},
],
)["project"]["arn"]
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
from prowler.providers.aws.services.codebuild.codebuild_service import Codebuild
with patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
), patch(
"prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled.codebuild_client",
new=Codebuild(aws_provider),
):
from prowler.providers.aws.services.codebuild.codebuild_project_logging_enabled.codebuild_project_logging_enabled import (
codebuild_project_logging_enabled,
)
check = codebuild_project_logging_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"CodeBuild project {project_name} does not have logging enabled."
)
assert result[0].resource_id == project_name
assert result[0].resource_arn == project_arn
assert result[0].resource_tags == [{"key": "Name", "value": "test"}]
assert result[0].region == AWS_REGION_EU_WEST_1

View File

@@ -5,6 +5,7 @@ import botocore
from prowler.providers.aws.services.codebuild.codebuild_service import (
Build,
CloudWatchLogs,
Codebuild,
Project,
s3Logs,
@@ -53,11 +54,16 @@ def mock_make_api_call(self, operation_name, kwarg):
}
],
"logsConfig": {
"cloudWatchLogs": {
"status": "ENABLED",
"groupName": project_name,
"streamName": project_name,
},
"s3Logs": {
"status": "ENABLED",
"location": "test-bucket",
"encryptionDisabled": False,
}
},
},
"tags": [{"key": "Name", "value": project_name}],
}
@@ -105,5 +111,15 @@ class Test_Codebuild_Service:
assert codebuild.projects[project_arn].s3_logs.enabled
assert codebuild.projects[project_arn].s3_logs.bucket_location == "test-bucket"
assert codebuild.projects[project_arn].s3_logs.encrypted
assert isinstance(
codebuild.projects[project_arn].cloudwatch_logs, CloudWatchLogs
)
assert codebuild.projects[project_arn].cloudwatch_logs.enabled
assert (
codebuild.projects[project_arn].cloudwatch_logs.group_name == project_name
)
assert (
codebuild.projects[project_arn].cloudwatch_logs.stream_name == project_name
)
assert codebuild.projects[project_arn].tags[0]["key"] == "Name"
assert codebuild.projects[project_arn].tags[0]["value"] == project_name