Skip to main content

CodeBuild Project Uses User-Controlled Buildspec

Overview

This check identifies AWS CodeBuild projects that use buildspec files stored in the source repository instead of inline (centrally managed) build specifications. When a buildspec file lives in the repository, anyone with commit access can modify the build instructions--potentially without proper review.

Risk

If this check fails, your CI/CD pipeline could execute malicious code through unreviewed pull requests. Attackers can weaponize repository-controlled buildspecs to:

  • Steal secrets: Access environment variables, AWS credentials, and other sensitive data
  • Tamper with artifacts: Inject malicious code into your build outputs
  • Pivot to other resources: Use the CodeBuild service role to access other AWS services
  • Mine cryptocurrency: Abuse your compute resources for unauthorized workloads

Severity: Medium

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify CodeBuild projects, or
  • AWS CLI installed and configured with appropriate credentials
Required IAM permissions

Your IAM user or role needs the following permissions:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"codebuild:UpdateProject",
"codebuild:BatchGetProjects"
],
"Resource": "*"
}
]
}

For production, scope the Resource to specific CodeBuild project ARNs.

AWS Console Method

  1. Open the AWS CodeBuild console
  2. Click on the project name identified in the Prowler finding
  3. Click Edit in the top right, then select Buildspec
  4. Under "Build specifications", select Insert build commands
  5. Click Switch to editor to enter your buildspec YAML
  6. Enter your build specification inline. At minimum:
    version: 0.2
    phases:
    build:
    commands:
    - echo "Build started"
  7. Click Update project

Repeat for each project flagged by Prowler.

AWS CLI method

Use the update-project command to set an inline buildspec:

aws codebuild update-project \
--name <your-project-name> \
--source '{
"type": "CODECOMMIT",
"location": "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/your-repo",
"buildspec": "version: 0.2\nphases:\n build:\n commands:\n - echo Build started"
}' \
--region us-east-1

Parameters:

  • --name: Your CodeBuild project name
  • --source: The source configuration with inline buildspec

Important: You must specify all required source fields (type, location) when updating. The buildspec value should be the complete inline YAML as a single string with \n for newlines.

Example with a more complete buildspec:

aws codebuild update-project \
--name my-application-build \
--source '{
"type": "GITHUB",
"location": "https://github.com/myorg/myrepo.git",
"buildspec": "version: 0.2\nphases:\n install:\n runtime-versions:\n nodejs: 18\n build:\n commands:\n - npm ci\n - npm run build\nartifacts:\n files:\n - \"**/*\"\n base-directory: dist"
}' \
--region us-east-1

Getting current project configuration first:

Before updating, retrieve the current source settings to preserve other fields:

aws codebuild batch-get-projects \
--names <your-project-name> \
--query 'projects[0].source' \
--region us-east-1
CloudFormation

Update your AWS::CodeBuild::Project resource to use an inline buildspec instead of a file reference.

Before (vulnerable - uses file in repository):

Resources:
MyCodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: my-application-build
Source:
Type: GITHUB
Location: https://github.com/myorg/myrepo.git
# No BuildSpec specified = uses buildspec.yml from repo

After (secure - inline buildspec):

Resources:
MyCodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: my-application-build
Source:
Type: GITHUB
Location: https://github.com/myorg/myrepo.git
BuildSpec: |
version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
build:
commands:
- npm ci
- npm run build
- npm test
artifacts:
files:
- '**/*'
base-directory: dist
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
Artifacts:
Type: S3
Location: !Ref ArtifactBucket
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0

Complete example with all common settings:

AWSTemplateFormatVersion: '2010-09-09'
Description: CodeBuild project with inline buildspec (secure configuration)

Parameters:
GitHubRepoUrl:
Type: String
Description: HTTPS URL of the GitHub repository

Resources:
ArtifactBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256

CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3FullAccess
Policies:
- PolicyName: CodeBuildLogs
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'

SecureCodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: secure-build-project
Description: Build project with centrally managed buildspec
Source:
Type: GITHUB
Location: !Ref GitHubRepoUrl
BuildSpec: |
version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
pre_build:
commands:
- echo "Installing dependencies..."
- npm ci
build:
commands:
- echo "Building application..."
- npm run build
post_build:
commands:
- echo "Running tests..."
- npm test
artifacts:
files:
- '**/*'
base-directory: dist
cache:
paths:
- 'node_modules/**/*'
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
Artifacts:
Type: S3
Location: !Ref ArtifactBucket
Packaging: ZIP
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
PrivilegedMode: false
LogsConfig:
CloudWatchLogs:
Status: ENABLED

Outputs:
ProjectName:
Value: !Ref SecureCodeBuildProject
Description: Name of the CodeBuild project
Terraform

Update your aws_codebuild_project resource to use an inline buildspec.

Before (vulnerable - uses file in repository):

resource "aws_codebuild_project" "my_project" {
name = "my-application-build"
service_role = aws_iam_role.codebuild_role.arn

source {
type = "GITHUB"
location = "https://github.com/myorg/myrepo.git"
# No buildspec specified = uses buildspec.yml from repo
}

# ... other configuration
}

After (secure - inline buildspec):

resource "aws_codebuild_project" "my_project" {
name = "my-application-build"
service_role = aws_iam_role.codebuild_role.arn

source {
type = "GITHUB"
location = "https://github.com/myorg/myrepo.git"
buildspec = <<-BUILDSPEC
version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
build:
commands:
- npm ci
- npm run build
- npm test
artifacts:
files:
- '**/*'
base-directory: dist
BUILDSPEC
}

artifacts {
type = "S3"
location = aws_s3_bucket.artifacts.bucket
}

environment {
type = "LINUX_CONTAINER"
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
privileged_mode = false
}
}

Complete module example:

variable "project_name" {
description = "Name of the CodeBuild project"
type = string
}

variable "github_repo_url" {
description = "HTTPS URL of the GitHub repository"
type = string
}

variable "buildspec_content" {
description = "Inline buildspec YAML content"
type = string
default = <<-BUILDSPEC
version: 0.2
phases:
build:
commands:
- echo "Build started"
BUILDSPEC
}

resource "aws_s3_bucket" "artifacts" {
bucket_prefix = "${var.project_name}-artifacts-"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "artifacts" {
bucket = aws_s3_bucket.artifacts.id

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

resource "aws_iam_role" "codebuild_role" {
name = "${var.project_name}-codebuild-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "codebuild.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}

resource "aws_iam_role_policy" "codebuild_policy" {
name = "${var.project_name}-codebuild-policy"
role = aws_iam_role.codebuild_role.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "*"
},
{
Effect = "Allow"
Action = [
"s3:PutObject",
"s3:GetObject"
]
Resource = "${aws_s3_bucket.artifacts.arn}/*"
}
]
})
}

resource "aws_codebuild_project" "secure_project" {
name = var.project_name
description = "Secure CodeBuild project with inline buildspec"
service_role = aws_iam_role.codebuild_role.arn

source {
type = "GITHUB"
location = var.github_repo_url
buildspec = var.buildspec_content
}

artifacts {
type = "S3"
location = aws_s3_bucket.artifacts.bucket
}

environment {
type = "LINUX_CONTAINER"
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
privileged_mode = false
}

logs_config {
cloudwatch_logs {
status = "ENABLED"
}
}

tags = {
SecurityCompliance = "inline-buildspec"
}
}

output "project_name" {
value = aws_codebuild_project.secure_project.name
description = "Name of the CodeBuild project"
}

Verification

After making changes, verify the configuration is correct:

  1. In the AWS Console, navigate to your CodeBuild project and confirm the Buildspec section shows "Insert build commands" rather than "Use a buildspec file"
  2. Re-run the Prowler check to confirm remediation:
    prowler aws --check codebuild_project_user_controlled_buildspec -r us-east-1
CLI verification

Use the batch-get-projects command to verify the buildspec configuration:

aws codebuild batch-get-projects \
--names <your-project-name> \
--query 'projects[0].source.buildspec' \
--region us-east-1

Expected result: The output should show your inline buildspec YAML content (starting with "version: 0.2"), NOT a file path like "buildspec.yml".

Check if buildspec references a file:

aws codebuild batch-get-projects \
--names <your-project-name> \
--query 'projects[0].source.buildspec' \
--output text \
--region us-east-1 | grep -E "\.ya?ml$"

If this returns any output, the project still uses a file-based buildspec.

Additional Resources

Notes

  • Trade-off with developer experience: Inline buildspecs are more secure but require infrastructure changes to update build steps. Consider storing buildspecs in a separate, tightly controlled repository and referencing them via S3 instead.

  • S3-hosted buildspec alternative: You can store your buildspec in an S3 bucket with strict access controls instead of inline. Use the S3 ARN format: arn:aws:s3:::my-secure-bucket/buildspec.yml. This provides central control while keeping buildspecs version-controlled separately.

  • Pull request builds: If you use CodeBuild for pull request validation, inline buildspecs prevent contributors from modifying the build to skip tests or introduce malicious code.

  • Webhook filtering: Even with inline buildspecs, consider using webhook filters to control which events trigger builds.

  • No immediate disruption: Updating the buildspec configuration takes effect on the next build. Running builds are not affected.