CodePipeline pipeline should use private repository source with authenticated connection
CodePipeline pipeline should configure its source stage to use a private repository with authenticated connection rather than a public GitHub or GitLab repository. This ensures deployment configurations, build artifacts, and CI/CD logic remain protected from unauthorized access.
Risk
Using public repositories as pipeline sources exposes deployment configurations and CI/CD workflows to the internet, increasing risk of supply chain attacks, credential exposure, and intellectual property theft. Adversaries can inject malicious code or leverage exposed secrets to compromise production systems.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks codepipeline_project_repo_privateRecommendation
Configure CodePipeline source stages to use private repositories with AWS CodeStar Connections for GitHub or GitLab.
Apply least privilege to connection permissions, enable branch protection, require code review, use signed commits, and monitor pipeline execution logs. Consider AWS CodeCommit for fully managed private Git hosting with native IAM integration.
Remediation
CLI
aws codestar-connections create-connection --provider-type GitHub --connection-name my-github-connection
aws codepipeline update-pipeline --pipeline file://pipeline-config.jsonTerraform
# Terraform: Configure pipeline with private repository via CodeStar Connection
resource "aws_codestarconnections_connection" "github" {
name = "my-github-connection"
provider_type = "GitHub" # or "GitLab"
}
resource "aws_codepipeline" "example" {
name = "my-pipeline"
role_arn = aws_iam_role.codepipeline.arn
stage {
name = "Source"
action {
name = "Source"
category = "Source"
owner = "AWS"
provider = "CodeStarSourceConnection"
version = "1"
output_artifacts = ["source_output"]
configuration = {
ConnectionArn = aws_codestarconnections_connection.github.arn
FullRepositoryId = "myorg/myrepo" # Private repository
BranchName = "main"
}
}
}
}NativeIaC
# CloudFormation: Configure pipeline with private repository via CodeStar Connection
Resources:
MyConnection:
Type: AWS::CodeStarConnections::Connection
Properties:
ConnectionName: my-github-connection
ProviderType: GitHub # or GitLab
MyPipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Stages:
- Name: Source
Actions:
- Name: SourceAction
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeStarSourceConnection
Version: 1
Configuration:
ConnectionArn: !GetAtt MyConnection.ConnectionArn
FullRepositoryId: myorg/myrepo # Private repository
BranchName: mainOther
Check Code
import ssl
import urllib.error
import urllib.request
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.codepipeline.codepipeline_client import (
codepipeline_client,
)
HTTP_TIMEOUT = 30
class codepipeline_project_repo_private(Check):
"""Checks if AWS CodePipeline source repositories are configured as private.
This check verifies whether source repositories (GitHub or GitLab) connected to
CodePipeline are publicly accessible. It attempts to access the repositories
anonymously to determine their visibility status.
Attributes:
None
"""
def execute(self) -> list:
"""Executes the repository privacy check for all CodePipeline sources.
Iterates through all CodePipeline pipelines and checks if their source
repositories (GitHub/GitLab) are publicly accessible by attempting anonymous
access.
Returns:
list: List of Check_Report_AWS objects containing the findings for each
pipeline's source repository.
"""
findings = []
for pipeline in codepipeline_client.pipelines.values():
if (
pipeline.source
and pipeline.source.type == "CodeStarSourceConnection"
and pipeline.source.repository_id
):
report = Check_Report_AWS(self.metadata(), resource=pipeline)
report.region = pipeline.region
report.resource_id = pipeline.name
report.resource_arn = pipeline.arn
report.resource_tags = pipeline.tags
# Try both GitHub and GitLab URLs
github_url = f"https://github.com/{pipeline.source.repository_id}"
gitlab_url = f"https://gitlab.com/{pipeline.source.repository_id}"
is_public_github = self._is_public_repo(github_url)
is_public_gitlab = self._is_public_repo(gitlab_url)
if is_public_github:
report.status = "FAIL"
report.status_extended = f"CodePipeline {pipeline.name} source repository is public: {github_url}"
elif is_public_gitlab:
report.status = "FAIL"
report.status_extended = f"CodePipeline {pipeline.name} source repository is public: {gitlab_url}"
else:
report.status = "PASS"
report.status_extended = f"CodePipeline {pipeline.name} source repository {pipeline.source.repository_id} is private."
findings.append(report)
return findings
def _is_public_repo(self, repo_url: str) -> bool:
"""Checks if a repository is publicly accessible.
Attempts to access the repository URL anonymously to determine if it's
public or private.
Args:
repo_url: String containing the repository URL to check.
Returns:
bool: True if the repository is public, False if private or inaccessible.
Note:
The method considers a repository private if:
- The URL redirects to a sign-in page
- The request fails with HTTP errors
- The URL is not accessible
"""
if repo_url.endswith(".git"):
repo_url = repo_url[:-4]
try:
req = urllib.request.Request(repo_url, method="HEAD")
response = urllib.request.urlopen(req, timeout=HTTP_TIMEOUT)
return not response.geturl().endswith("sign_in")
except (urllib.error.URLError, TimeoutError, ssl.SSLError):
return False