AWS provider
The AWS provider uses a boto3.Session scoped to a single region. It
supports named AWS CLI profiles via --profile and honors the
AWS_ENDPOINT_URL environment variable (for LocalStack).
Every AWS rule is one module under
pipeline_check/core/checks/aws/rules/<id>_<slug>.py, auto-discovered by
AWSRuleChecks and given a shared ResourceCatalog so enumerations run
once per scan.
Services covered
| Service | Check IDs |
|---|---|
| CodeBuild | CB-001..011 |
| CodePipeline | CP-001..007 |
| CodeDeploy | CD-001, CD-002, CD-003 |
| ECR | ECR-001..007 |
| IAM | IAM-001..008 |
| PBAC (CodeBuild roles/VPC, pipeline role scoping) | PBAC-001..005 |
| S3 | S3-001, S3-002, S3-003, S3-004, S3-005 |
| CloudTrail | CT-001, CT-002, CT-003 |
| CloudWatch Logs | CWL-001, CWL-002 |
| CloudWatch Alarms | CW-001 |
| Secrets Manager | SM-001, SM-002 |
| CodeArtifact | CA-001, CA-002, CA-003, CA-004 |
| CodeCommit | CCM-001, CCM-002, CCM-003 |
| Lambda | LMB-001, LMB-002, LMB-003, LMB-004 |
| KMS | KMS-001, KMS-002 |
| SSM Parameter Store | SSM-001, SSM-002 |
| EventBridge | EB-001, EB-002 |
| AWS Signer | SIGN-001, SIGN-002 |
CLI usage
# Default: scan us-east-1 with the ambient boto3 credential chain
pipeline_check --pipeline aws
# Pick a region
pipeline_check --pipeline aws --region eu-west-1
# Use a named AWS CLI profile (~/.aws/credentials)
pipeline_check --pipeline aws --profile prod-readonly
# Scope the scan to a single resource (e.g. one CodePipeline)
pipeline_check --pipeline aws --target my-release-pipeline
# Point boto3 at a local endpoint (LocalStack, etc.)
AWS_ENDPOINT_URL=http://localhost:4566 pipeline_check --pipeline aws
Credentials are resolved through the standard
boto3 credential chain. Environment variables, ~/.aws/credentials, IMDS on EC2, container
credentials on ECS/Fargate, EKS Pod Identity, or SSO. To scan a different
account, assume the role first with aws sts assume-role (or
aws sso login / aws-vault) and export the resulting credentials, then
invoke pipeline_check. The provider does not currently accept an
--assume-role-arn flag.
Required IAM permissions
The scanner is read-only: every API call is a List*, Describe*,
Get*, or BatchGet*. It never mutates state. Per-resource API calls
are scoped to the active --region; IAM, S3, and STS are global and are
reached through the same regional session.
Quickest path: managed policies
If you don't care about least-privilege, attach one of:
arn:aws:iam::aws:policy/SecurityAudit: covers everything below except a fewGet*Policycalls; produces the same findings minusS3-005(bucket policies),LMB-004(Lambda resource policy),KMS-002(key policy),CA-003/CA-004(CodeArtifact policies),ECR-003(repo policy),SM-002(secret resource policy).arn:aws:iam::aws:policy/ReadOnlyAccess: covers every action the scanner uses, plus a great deal more. Convenient but broad.
For least-privilege, use the policy below.
Permission map by service
Each row lists the check IDs that depend on the actions in that service.
If you skip a service entirely, you can drop its row from the policy and
the scanner will emit a <PREFIX>-000 degraded finding (INFO) for that
service rather than failing.
| Service | Check IDs that need it | Required actions |
|---|---|---|
| CodeBuild | CB-001..011, PBAC-001 | codebuild:ListProjects, codebuild:BatchGetProjects, codebuild:ListSourceCredentials |
| CodePipeline | CP-001..007, PBAC-005 (also feeds S3 artifact-bucket discovery) | codepipeline:ListPipelines, codepipeline:GetPipeline |
| CodeDeploy | CD-001..003 | codedeploy:ListApplications, codedeploy:ListDeploymentGroups, codedeploy:BatchGetDeploymentGroups |
| ECR | ECR-001..007 | ecr:DescribeRepositories, ecr:GetRepositoryPolicy, ecr:GetLifecyclePolicy, ecr:DescribePullThroughCacheRules |
| Inspector v2 | ECR-007 | inspector2:BatchGetAccountStatus |
| IAM | IAM-001..008, PBAC-002, CICD-role enumeration | iam:ListRoles, iam:ListUsers, iam:ListAccessKeys, iam:GetAccessKeyLastUsed, iam:ListRolePolicies, iam:GetRolePolicy, iam:ListAttachedRolePolicies, iam:GetPolicy, iam:GetPolicyVersion |
| CloudTrail | CT-001..003 | cloudtrail:DescribeTrails, cloudtrail:GetTrailStatus |
| CloudWatch Logs | CWL-001, CWL-002 | logs:DescribeLogGroups |
| CloudWatch Alarms | CW-001 | cloudwatch:DescribeAlarms |
| Secrets Manager | SM-001, SM-002 | secretsmanager:ListSecrets, secretsmanager:GetResourcePolicy |
| CodeArtifact | CA-001..004 | codeartifact:ListDomains, codeartifact:ListRepositories, codeartifact:DescribeRepository, codeartifact:GetDomainPermissionsPolicy, codeartifact:GetRepositoryPermissionsPolicy |
| CodeCommit | CCM-001..003 | codecommit:ListRepositories, codecommit:GetRepository, codecommit:GetRepositoryTriggers, codecommit:ListAssociatedApprovalRuleTemplatesForRepository |
| Lambda | LMB-001..004 | lambda:ListFunctions, lambda:GetFunctionCodeSigningConfig, lambda:GetFunctionUrlConfig, lambda:GetPolicy |
| KMS | KMS-001, KMS-002 | kms:ListKeys, kms:DescribeKey, kms:GetKeyRotationStatus, kms:GetKeyPolicy |
| SSM Parameter Store | SSM-001, SSM-002 | ssm:DescribeParameters |
| EventBridge | EB-001, EB-002 | events:ListRules, events:ListTargetsByRule |
| Signer | SIGN-001, SIGN-002 | signer:ListSigningProfiles |
| S3 | S3-001..005 (artifact buckets discovered via CodePipeline) | s3:GetPublicAccessBlock, s3:GetBucketEncryption, s3:GetBucketVersioning, s3:GetBucketLogging, s3:GetBucketPolicy |
| EC2 | PBAC-003 (CodeBuild VPC security groups) | ec2:DescribeSecurityGroups |
| STS | CCM-003 (current account ID for cross-account trigger detection) | sts:GetCallerIdentity |
Copy-paste IAM policy
Save the following as pipeline-check-readonly.json and attach it to the
role or user the scanner runs as. Every action is read-only and every
resource is * because boto3 list/describe APIs do not accept
resource-level conditions.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PipelineCheckReadOnlyScan",
"Effect": "Allow",
"Action": [
"cloudtrail:DescribeTrails",
"cloudtrail:GetTrailStatus",
"cloudwatch:DescribeAlarms",
"codeartifact:DescribeRepository",
"codeartifact:GetDomainPermissionsPolicy",
"codeartifact:GetRepositoryPermissionsPolicy",
"codeartifact:ListDomains",
"codeartifact:ListRepositories",
"codebuild:BatchGetProjects",
"codebuild:ListProjects",
"codebuild:ListSourceCredentials",
"codecommit:GetRepository",
"codecommit:GetRepositoryTriggers",
"codecommit:ListAssociatedApprovalRuleTemplatesForRepository",
"codecommit:ListRepositories",
"codedeploy:BatchGetDeploymentGroups",
"codedeploy:ListApplications",
"codedeploy:ListDeploymentGroups",
"codepipeline:GetPipeline",
"codepipeline:ListPipelines",
"ec2:DescribeSecurityGroups",
"ecr:DescribePullThroughCacheRules",
"ecr:DescribeRepositories",
"ecr:GetLifecyclePolicy",
"ecr:GetRepositoryPolicy",
"events:ListRules",
"events:ListTargetsByRule",
"iam:GetAccessKeyLastUsed",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRolePolicy",
"iam:ListAccessKeys",
"iam:ListAttachedRolePolicies",
"iam:ListRolePolicies",
"iam:ListRoles",
"iam:ListUsers",
"inspector2:BatchGetAccountStatus",
"kms:DescribeKey",
"kms:GetKeyPolicy",
"kms:GetKeyRotationStatus",
"kms:ListKeys",
"lambda:GetFunctionCodeSigningConfig",
"lambda:GetFunctionUrlConfig",
"lambda:GetPolicy",
"lambda:ListFunctions",
"logs:DescribeLogGroups",
"s3:GetBucketEncryption",
"s3:GetBucketLogging",
"s3:GetBucketPolicy",
"s3:GetBucketVersioning",
"s3:GetPublicAccessBlock",
"secretsmanager:GetResourcePolicy",
"secretsmanager:ListSecrets",
"signer:ListSigningProfiles",
"ssm:DescribeParameters",
"sts:GetCallerIdentity"
],
"Resource": "*"
}
]
}
The policy is 2.5 KB, well inside the 6,144-byte limit for a customer-managed policy and the 10,240-byte limit for an inline role policy.
Trust policy for an IAM role
If you run the scanner from CI (e.g. GitHub Actions with OIDC), pair the policy above with a trust policy that lets your CI system assume the role. Below is an example for GitHub Actions OIDC; adapt for your provider.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
}
}
}
]
}
Behavior when permissions are missing
The scanner does not fail closed when the principal lacks an action.
Instead, the per-service enumeration records the error and the
orchestrator emits one <PREFIX>-000 finding (INFO severity) per
degraded service, for example CT-000, LMB-000, KMS-000. Every
rule that depends on that service is suppressed for the run. Operators
can therefore see exactly which permission gaps are masking findings.
Two exceptions are tolerated silently because their endpoints are optional:
ecr:DescribePullThroughCacheRules: not all regions/accounts have PTC; only ECR-006 is suppressed if it fails.inspector2:BatchGetAccountStatus: only ECR-007 is suppressed if Inspector v2 is not enabled in the region.
What it covers
71 checks · 0 have an autofix patch (--fix).
| Check | Title | Severity | Fix |
|---|---|---|---|
| CA-001 | CodeArtifact domain has no KMS encryptionKey configured | MEDIUM | |
| CA-002 | CodeArtifact repository has a public external connection | HIGH | |
| CA-003 | CodeArtifact domain policy allows cross-account wildcard | CRITICAL | |
| CA-004 | CodeArtifact repo policy grants codeartifact:* with Resource '*' |
HIGH | |
| CB-001 | Secrets in plaintext environment variables | CRITICAL | |
| CB-002 | Privileged mode enabled | HIGH | |
| CB-003 | Build logging not enabled | MEDIUM | |
| CB-004 | Build timeout missing or at the AWS maximum (480 min) | LOW | |
| CB-005 | Outdated managed build image | MEDIUM | |
| CB-006 | CodeBuild source auth uses long-lived token | HIGH | |
| CB-007 | CodeBuild webhook has no filter group | MEDIUM | |
| CB-008 | CodeBuild buildspec is inline (not sourced from a protected repo) | HIGH | |
| CB-009 | CodeBuild image not pinned by digest | MEDIUM | |
| CB-010 | CodeBuild webhook allows fork-PR builds without actor filtering | HIGH | |
| CB-011 | CodeBuild buildspec contains indicators of malicious activity | CRITICAL | |
| CCM-001 | CodeCommit repository has no approval rule template attached | HIGH | |
| CCM-002 | CodeCommit repository not encrypted with customer KMS CMK | MEDIUM | |
| CCM-003 | CodeCommit trigger targets SNS/Lambda in a different account | MEDIUM | |
| CD-001 | Automatic rollback on failure not enabled | MEDIUM | |
| CD-002 | AllAtOnce deployment config, no canary or rolling strategy | HIGH | |
| CD-003 | No CloudWatch alarm monitoring on deployment group | MEDIUM | |
| CP-001 | No approval action before deploy stages | HIGH | |
| CP-002 | Artifact store not encrypted with customer-managed KMS key | MEDIUM | |
| CP-003 | Source stage using polling instead of event-driven trigger | LOW | |
| CP-004 | Legacy ThirdParty/GitHub source action (OAuth token) | HIGH | |
| CP-005 | Production Deploy stage has no preceding ManualApproval | MEDIUM | |
| CP-007 | CodePipeline v2 PR trigger accepts all branches | HIGH | |
| CT-001 | No active CloudTrail trail in region | HIGH | |
| CT-002 | CloudTrail log-file validation disabled | MEDIUM | |
| CT-003 | CloudTrail trail is not multi-region | MEDIUM | |
| CW-001 | No CloudWatch alarm on CodeBuild FailedBuilds metric | LOW | |
| CWL-001 | CodeBuild log group has no retention policy | LOW | |
| CWL-002 | CodeBuild log group not KMS-encrypted | MEDIUM | |
| EB-001 | No EventBridge rule for CodePipeline failure notifications | MEDIUM | |
| EB-002 | EventBridge rule has a wildcard target ARN | HIGH | |
| ECR-001 | Image scanning on push not enabled | HIGH | |
| ECR-002 | Image tags are mutable | HIGH | |
| ECR-003 | Repository policy allows public access | CRITICAL | |
| ECR-004 | No lifecycle policy configured | LOW | |
| ECR-005 | Repository encrypted with AES256 rather than KMS CMK | MEDIUM | |
| ECR-006 | ECR pull-through cache rule uses an untrusted upstream | HIGH | |
| ECR-007 | Inspector v2 enhanced scanning disabled for ECR | MEDIUM | |
| IAM-001 | CI/CD role has AdministratorAccess policy attached | CRITICAL | |
| IAM-002 | CI/CD role has wildcard Action in attached policy | HIGH | |
| IAM-003 | CI/CD role has no permission boundary | MEDIUM | |
| IAM-004 | CI/CD role can PassRole to any role | HIGH | |
| IAM-005 | CI/CD role trust policy missing sts:ExternalId | HIGH | |
| IAM-006 | Sensitive actions granted with wildcard Resource | MEDIUM | |
| IAM-007 | IAM user has access key older than 90 days | HIGH | |
| IAM-008 | OIDC-federated role trust policy missing audience or subject pin | HIGH | |
| KMS-001 | KMS customer-managed key has rotation disabled | MEDIUM | |
| KMS-002 | KMS key policy grants wildcard KMS actions | HIGH | |
| LMB-001 | Lambda function has no code-signing config | HIGH | |
| LMB-002 | Lambda function URL has AuthType=NONE | HIGH | |
| LMB-003 | Lambda function env vars may contain plaintext secrets | HIGH | |
| LMB-004 | Lambda resource policy allows wildcard principal | CRITICAL | |
| PBAC-001 | CodeBuild project has no VPC configuration | HIGH | |
| PBAC-002 | CodeBuild service role shared across multiple projects | MEDIUM | |
| PBAC-003 | CodeBuild security group allows 0.0.0.0/0 all-port egress | MEDIUM | |
| PBAC-005 | CodePipeline stage action roles mirror the pipeline role | HIGH | |
| S3-001 | Artifact bucket public access block not fully enabled | CRITICAL | |
| S3-002 | Artifact bucket server-side encryption not configured | HIGH | |
| S3-003 | Artifact bucket versioning not enabled | MEDIUM | |
| S3-004 | Artifact bucket access logging not enabled | LOW | |
| S3-005 | Artifact bucket missing aws:SecureTransport deny | MEDIUM | |
| SIGN-001 | No AWS Signer profile defined for Lambda deploys | MEDIUM | |
| SIGN-002 | AWS Signer profile is revoked or inactive | HIGH | |
| SM-001 | Secrets Manager secret has no rotation configured | HIGH | |
| SM-002 | Secrets Manager resource policy allows wildcard principal | CRITICAL | |
| SSM-001 | SSM Parameter with secret-like name is not a SecureString | HIGH | |
| SSM-002 | SSM SecureString uses the default AWS-managed key | MEDIUM |
CA-001: CodeArtifact domain has no KMS encryptionKey configured
When no encryptionKey is configured on the domain, AWS uses its own managed key, keeping the key policy under AWS's control. That removes your ability to scope or audit Decrypt operations, and you can't revoke key access without recreating the domain. A customer-managed CMK puts those controls back in your hands. Note: the CodeArtifact API returns the resolved KMS key ARN in this field; the check flags only the absent-key case because the ARN alone does not reliably identify whether the key is AWS-managed or customer-managed without a separate kms:DescribeKey call.
Recommended action
Recreate the CodeArtifact domain with an encryption-key argument pointing at a customer-managed CMK. Domain encryption is set at creation and cannot be changed after.
CA-002: CodeArtifact repository has a public external connection
An external connection to public:npmjs / public:pypi / public:nuget / public:maven-central fetches packages from the public registry on first resolution. A typo-squat (request vs requests) or a compromised upstream lands in the cache the first time anyone names it; every subsequent build pulls the cached substitute. The pull-through cache with an allow-list is the same risk shape solved by an explicit allowlist.
Recommended action
Route public package consumption through a pull-through cache repository governed by an allow-list of package names, and point build-time repos at that cache rather than directly at public:npmjs/public:pypi. Unscoped public upstreams expose builds to dependency-confusion and typosquatting attacks.
CA-003: CodeArtifact domain policy allows cross-account wildcard
A wildcard-principal Allow on a CodeArtifact domain lets any AWS account reach the domain's permissions surface. The exact damage depends on the action set, but at minimum it lets external accounts read package names and versions, which is enough for typosquat-against-private-package attacks. aws:PrincipalOrgID is the org-level rescue without enumerating accounts.
Recommended action
Remove Allow statements with Principal: '*' from every CodeArtifact domain permissions policy, or restrict them with an aws:PrincipalOrgID condition so only accounts in your org can consume packages from the domain.
CA-004: CodeArtifact repo policy grants codeartifact:* with Resource '*'
codeartifact:* on Resource: '*' collapses the entire repository's authority into one grant: the holder can read, write, delete, dispose, and re-publish every package. Even for a service principal that nominally only consumes packages, the grant lets a compromise of that consumer rewrite every dependency the team relies on.
Recommended action
Scope Allow statements to specific codeartifact: actions (e.g. codeartifact:ReadFromRepository) and to specific package-group ARNs. Wildcard action + wildcard resource is the classic over-broad grant that lets a consumer also publish.
CB-001: Secrets in plaintext environment variables
Flags a plaintext env var when either (a) its name matches a secret-like pattern (PASSWORD, TOKEN, API_KEY, ...) or (b) its value matches a known credential shape (AKIA/ASIA access keys, GitHub tokens, Slack xox* tokens, JWTs). Plaintext values are visible in the AWS console, CloudTrail, and build logs to anyone with read access.
Recommended action
Move secrets to AWS Secrets Manager or SSM Parameter Store and reference them using type SECRETS_MANAGER or PARAMETER_STORE in the CodeBuild environment variable configuration.
CB-002: Privileged mode enabled
Privileged mode grants the build container root access to the host's Docker daemon. A compromised build can escape the container or tamper with the host. Only flip this on for real Docker-in-Docker workloads and keep the buildspec under branch-protected review.
Recommended action
Disable privileged mode unless the project explicitly requires Docker-in-Docker builds. If required, ensure the buildspec is tightly controlled, peer-reviewed, and sourced from a trusted repository with branch protection.
CB-003: Build logging not enabled
A CodeBuild project with neither CloudWatch Logs nor S3 logging enabled leaves no durable record of what the build did. The CodeBuild console shows the last execution's logs for a short retention window, but anything older, and any automated review of historical activity during incident response, is gone.
Recommended action
Enable CloudWatch Logs or S3 logging in the CodeBuild project configuration to maintain a durable audit trail of all build activity.
CB-004: Build timeout missing or at the AWS maximum (480 min)
A CodeBuild project at AWS's 480-minute maximum is rarely deliberate. Without a tighter ceiling, a runaway test loop, a fork-PR cryptomining payload, or a build that hangs on stdin keeps the build host (and its IAM role) live for the full eight hours, racking up cost and extending the compromise window.
Recommended action
Set a build timeout appropriate for your expected build duration (typically 15–60 minutes) to limit the blast radius of a runaway or abused build.
CB-005: Outdated managed build image
Only AWS-managed aws/codebuild/standard:N.0 images are version-checked. Custom or third-party images pass here, CB-009 handles the separate concern of tag vs digest pinning for custom images.
Known false-positive modes
- One version behind the current
aws/codebuild/standardis a hygiene warning, not a production issue, and defaults to MEDIUM confidence. The rule emits HIGH only when the project is two or more versions behind. Custom or third-party images are not version-checked here; CB-009 handles tag-vs-digest pinning for those.
Recommended action
Update the CodeBuild environment image to aws/codebuild/standard:7.0 or later to ensure the build environment receives the latest security patches.
CB-006: CodeBuild source auth uses long-lived token
OAUTH / PERSONAL_ACCESS_TOKEN / BASIC_AUTH source credentials are stored long-lived on the account and used by every CodeBuild project that points at the SCM provider. Rotating the upstream PAT requires manual re-credentialing here too. CodeConnections (CodeStar) is the AWS-managed alternative with token refresh and revocation.
Recommended action
Switch to an AWS CodeConnections (CodeStar) connection and reference it from the source configuration. Delete any stored source credentials of type OAUTH, PERSONAL_ACCESS_TOKEN, or BASIC_AUTH via delete_source_credentials.
CB-007: CodeBuild webhook has no filter group
A CodeBuild webhook with no filter groups fires on every push and every PR from any actor, including fork PRs from outside the org. Anyone able to open a PR triggers the build with whatever IAM authority the project's role carries. Filter groups (branch + actor + event type) are the gate.
Recommended action
Define filter groups restricting triggers to specific branches, actors, and event types.
CB-008: CodeBuild buildspec is inline (not sourced from a protected repo)
An inline buildspec (source.buildspec set to YAML text, or a S3 URL) bypasses the protections that cover your source code. A user with codebuild:UpdateProject can rewrite the build commands without touching the repository, no PR review, no branch protection, no audit of what changed. Store buildspec.yml in the repo instead.
Recommended action
Remove the inline buildspec and store buildspec.yml in the source repository under branch protection. Anyone with codebuild:UpdateProject can silently rewrite an inline buildspec; repository-sourced buildspecs inherit the repo's review and protection controls.
CB-009: CodeBuild image not pinned by digest
CodeBuild pulls the environment image on every build. A tag pointer can be moved by whoever controls the registry; a digest cannot. AWS-managed aws/codebuild/... images are exempt. Those are covered by CB-005 and are not part of the tag-mutation threat model.
Recommended action
Pin custom CodeBuild images by @sha256:<digest>. Tag-based references (:latest, :1.2.3) can be silently overwritten to point at a malicious layer that is pulled on the next build.
CB-010: CodeBuild webhook allows fork-PR builds without actor filtering
GitHub/Bitbucket webhook filter groups that fire on pull-request events will build forks by default. Because CodeBuild runs with the project's own IAM role (not the PR author's), a fork PR can execute arbitrary code with CI privileges and exfiltrate secrets. Restrict to known contributors with an ACTOR_ACCOUNT_ID pattern group.
Recommended action
Add an ACTOR_ACCOUNT_ID filter pattern to every webhook filter group that accepts PULL_REQUEST_CREATED / PULL_REQUEST_UPDATED / PULL_REQUEST_REOPENED, or remove those PR event types. Without actor filtering, any fork can trigger a build that runs with the project's service role.
CB-011: CodeBuild buildspec contains indicators of malicious activity
Scans the source.buildspec text on every CodeBuild project for concrete attack indicators: reverse shells, base64-decoded execution, miner binaries/pools, Discord/Telegram webhooks, credential-dump pipes, audit-erasure commands. CB-011 is CRITICAL by design, a true positive is evidence of compromise, not a hygiene improvement. Repo-sourced buildspecs (not inlined) return NOT APPLICABLE because the text isn't visible to the scanner; CB-008 already flags the inline form as a governance gap.
Known false-positive modes
- Security-training repositories, CTF challenges, and red-team exercise pipelines legitimately contain reverse-shell strings or exfil domains as literals. Matches inside YAML keys / HCL attributes whose names contain
example,fixture,sample,demo, ortestare auto-suppressed; bare lines in a production pipeline still fire. - Defaults to LOW confidence. Filter with
--min-confidence MEDIUMto ignore all matches; the rule still surfaces the hit for teams that want to spot-check.
Recommended action
Treat as a potential compromise. Identify which principal or pipeline ran the CodeBuild project recently, rotate its service role's credentials, audit CloudTrail for outbound activity to the matched hosts, and, if an inline buildspec is in use (CB-008), enforce repo-sourced buildspecs under branch protection so the next malicious edit requires a PR.
CCM-001: CodeCommit repository has no approval rule template attached
Approval-rule templates are CodeCommit's analog of GitHub's branch-protection require-review. Without one associated, the repository accepts merges from any push-permitted principal, including the PR author themselves, without any second-pair-of-eyes gate.
Recommended action
Create a CodeCommit approval-rule template requiring at least one approval from a designated pool of reviewers and associate it with every repository. Without one, any PR author with push rights can self-approve and merge.
CCM-002: CodeCommit repository not encrypted with customer KMS CMK
Same shape as CA-001 / ECR-005 / S3 default encryption: the AWS-owned default key keeps the key policy under AWS, removing your ability to scope or audit Decrypt operations. Source code in the repo deserves the same key-policy + CloudTrail story you'd apply to artifacts in S3. Note: the CodeCommit API returns the resolved KMS key ARN in kmsKeyId; the check flags only the absent-key case because the ARN alone does not reliably identify whether the key is AWS-managed or customer-managed without a separate kms:DescribeKey call.
Recommended action
Recreate the repository with a kmsKeyId argument pointing at a customer-managed KMS key. CodeCommit encryption is set at creation and cannot be changed afterwards.
CCM-003: CodeCommit trigger targets SNS/Lambda in a different account
A repo trigger pointing at an SNS topic or Lambda in a different account fires under the receiving account's permissions on every push. Sometimes this is the intended shape (a centralized notifications account), but a cross-account fan-out from a compromised repo can drive actions in the receiving account that the source-account owner can't directly observe.
Recommended action
Move trigger targets into the same account as the repository or explicitly document the cross-account relationship. Cross-account triggers extend the blast radius of a repository compromise to whatever the target ARN can do.
CD-001: Automatic rollback on failure not enabled
Without autoRollbackConfiguration, a CodeDeploy deployment that fails leaves the failed revision live until an operator notices. The default is opt-in, not opt-out, deployments fail-open, not fail-back.
Recommended action
Enable autoRollbackConfiguration with at least the DEPLOYMENT_FAILURE event so CodeDeploy automatically reverts to the last successful revision when a deployment fails.
CD-002: AllAtOnce deployment config, no canary or rolling strategy
AllAtOnce shifts 100% of traffic to the new revision in one step. There's no gradient to halt on if a CloudWatch alarm trips mid-rollout, the bad revision is already serving every request. Canary / linear configs introduce the shift-then-watch shape that lets monitors catch a regression before it's universal.
Recommended action
Switch to a canary or linear deployment configuration (e.g. CodeDeployDefault.LambdaCanary10Percent5Minutes or a custom rolling config) so that defects are caught before they affect all instances or traffic.
CD-003: No CloudWatch alarm monitoring on deployment group
Alarm-based rollback is what lets a canary configuration actually stop a bad deploy mid-flight. Without alarms wired into alarmConfiguration, CodeDeploy's only signal that the deploy went wrong is the deployment-state machine itself, which doesn't notice an application-level regression. CD-002's canary work and this rule's alarm-based halt are paired.
Recommended action
Add CloudWatch alarms (e.g. error rate, 5xx count, latency p99) to the deployment group's alarmConfiguration. Enable automatic rollback on DEPLOYMENT_STOP_ON_ALARM to halt bad deployments.
CP-001: No approval action before deploy stages
A pipeline that goes Source -> Build -> Deploy with no Approval action means every commit on the source branch ships, with no human ack between code-merged and code-running-in-prod. The Manual approval action is the intentional pause point, combine with CP-005 for production-tagged stages specifically.
Recommended action
Add a Manual approval action to a stage that precedes every Deploy stage that targets a production or sensitive environment.
CP-002: Artifact store not encrypted with customer-managed KMS key
The pipeline's S3 artifact store holds intermediate build outputs handed between stages. Default SSE-S3 (AES256) encrypts at rest but uses an AWS-owned key whose policy you can't scope. A customer-managed CMK gives the same key-policy + CloudTrail Decrypt-event audit story you'd apply to Lambda code, Secrets Manager, or any other build output.
Recommended action
Configure a customer-managed AWS KMS key as the encryptionKey for each artifact store. This enables key rotation, fine-grained access policies, and CloudTrail auditing of decrypt operations.
CP-003: Source stage using polling instead of event-driven trigger
PollForSourceChanges=true polls the source repo every minute or two. Beyond the API-quota and latency cost, polling produces a less-useful CloudTrail story than event-driven triggers. You see the poll calls, not the specific commit that started the pipeline. EventBridge / CodeCommit triggers tie each pipeline start to the originating event.
Known false-positive modes
PollForSourceChanges=trueis the CFN default for CodeCommit sources, so legacy templates can carry the flag without an active design decision behind it. The rule is advisory (consider EventBridge / CodeStarSourceConnection) rather than a real risk; defaults to LOW confidence so CI gates default-filter it.
Recommended action
Set PollForSourceChanges=false and configure an Amazon EventBridge rule or CodeCommit trigger to start the pipeline on change. This reduces latency, API usage, and improves auditability.
CP-004: Legacy ThirdParty/GitHub source action (OAuth token)
The legacy ThirdParty/GitHub source-action provider stores a long-lived OAuth token in the pipeline's action configuration. The token has whatever scope the granting GitHub user has, never rotates, and isn't directly revocable from the AWS side. CodeConnections (formerly CodeStar Connections) replaces this with an AWS-managed connection that the GitHub user can revoke.
Recommended action
Migrate to owner=AWS, provider=CodeStarSourceConnection and reference a CodeConnections connection ARN.
CP-005: Production Deploy stage has no preceding ManualApproval
The complement to CP-001: this rule fires only on stages whose name contains prod / production / live. Even teams that intentionally skip approvals for dev / staging deploys usually want a human in the loop for a production-tagged target.
Recommended action
Add a Manual approval action immediately before any stage whose name contains prod / production / live. CP-001 covers the generic case; this rule specifically looks at production-tagged stages where the blast radius of an unreviewed deploy is largest.
CP-007: CodePipeline v2 PR trigger accepts all branches
V2 pipelines added native PR triggers; without a branches.includes filter, any PR, including fork PRs from outside the org, fires the pipeline. The build stage runs with whatever IAM authority the pipeline's role carries, which is the full attack surface a fork-PR compromise can reach.
Recommended action
On V2 pipelines, add an includes filter under the trigger's branches block (and optionally pullRequest.events) so only PRs targeting specific branches run. Without a filter, any fork-PR can execute the pipeline's build and deploy stages.
CT-001: No active CloudTrail trail in region
CloudTrail is the only AWS-native source of record for management-plane API calls. A region with no active trail blinds incident responders: a pipeline compromise is invisible once the in-memory CloudWatch buffer rolls over.
Recommended action
Create a CloudTrail trail that logs management events in this region and start logging. Without a trail, CodeBuild/CodePipeline/IAM API activity, including credential changes during a compromise, has no durable audit record.
CT-002: CloudTrail log-file validation disabled
CloudTrail logs are S3 objects. Without log-file validation, an attacker with s3:PutObject on the trail bucket can edit log files to remove evidence of their activity, and there's no digest to compare against. With validation on, every hour of logs is summarized in a signed digest file under CloudTrail-Digest/.
Recommended action
Set LogFileValidationEnabled=true on every CloudTrail trail. Log validation produces a signed digest file alongside each log object so tampering by an attacker who also has S3 write access can be detected after the fact.
CT-003: CloudTrail trail is not multi-region
An attacker who knows your CloudTrail trail is regional deliberately operates from a different region. Multi-region trails capture management events from every region into a single trail, closing the gap without you having to enumerate which regions you actually use.
Recommended action
Convert the trail to a multi-region trail. A single-region trail misses activity in every other region, an attacker aware of the scope can drive reconnaissance or persistence from an unlogged region.
CW-001: No CloudWatch alarm on CodeBuild FailedBuilds metric
Failure-rate signals are how on-call learns about an unfamiliar build crashing in a loop, an attacker probing the build environment, or a CI quota being exhausted. CloudWatch captures the FailedBuilds metric automatically, the alarm is the missing fan-out.
Recommended action
Create a CloudWatch alarm on the AWS/CodeBuild namespace FailedBuilds metric (aggregated or per-project). Without one, repeated build failures during a compromise, or a runaway fork-PR build, won't reach on-call.
CWL-001: CodeBuild log group has no retention policy
CloudWatch Logs created by CodeBuild default to Never Expire retention. Build logs frequently echo secrets accidentally (a set -x script, an env dump in an error trace), so unbounded retention extends the exposure window for every secret a build has ever leaked. A short-but-finite retention also caps cost.
Recommended action
Set a retention policy on every /aws/codebuild/* log group. The default is 'Never Expire', which both racks up storage cost and keeps logs indefinitely past any compliance window.
CWL-002: CodeBuild log group not KMS-encrypted
CloudWatch Logs default encryption is service-managed, fine for confidentiality, but no audit trail or scoping. Build logs are a frequent secret-leak vector (CWL-001's rationale extended), so the same key-policy + Decrypt-event story you'd apply to S3 / Lambda / Secrets Manager is warranted here too.
Recommended action
Associate a customer-managed KMS key with every /aws/codebuild/* log group via associate-kms-key. Logs often contain secret material accidentally echoed by builds; encrypting them with a CMK means the key policy controls who can read the logs, not just S3/CloudWatch IAM.
EB-001: No EventBridge rule for CodePipeline failure notifications
Pipeline failure events are emitted to EventBridge automatically; the missing piece is a rule that pipes them to somewhere a human reads (SNS, Slack, PagerDuty). Without it, failures only surface via the CodePipeline console, which no one watches.
Recommended action
Create an EventBridge rule matching detail-type: 'CodePipeline Pipeline Execution State Change' and state: FAILED, and point it at an SNS topic or chat webhook. Without it, pipeline failures during an incident (a compromise triggering rollback, for example) go unnoticed.
EB-002: EventBridge rule has a wildcard target ARN
Wildcard target ARNs (e.g. arn:aws:lambda:us-east-1:123456789012:function:*) match every resource that fits the prefix. This is rarely intentional, usually a copy-paste from a more permissive resource ARN, and means the rule fans out to a much larger set of consumers than the author meant.
Recommended action
Replace wildcard target ARNs with specific resource ARNs. EventBridge targets with * route events to any resource that matches the prefix, frequently triggering unintended Lambda invocations or SNS sends.
ECR-001: Image scanning on push not enabled
scan-on-push runs a CVE check against the image's OS package layers at the moment it lands in ECR. Without it, an image with a known CVE deploys silently. The ECR basic scanner is free; ECR-007 covers the Inspector v2 enhanced scanner that adds language-ecosystem CVEs (npm, pip, gem).
Recommended action
Enable imageScanningConfiguration.scanOnPush on the repository. Consider also enabling Amazon Inspector continuous scanning for ongoing CVE detection against images already in the registry.
ECR-002: Image tags are mutable
Mutable tags mean :latest, :v1.0, and :stable can be re-pushed silently, the same tag points to different image content over time. Pinning by digest (sha256:...) in deployment manifests is the only durable reference; IMMUTABLE on the repo enforces the property registry-side so a forgotten digest reference doesn't drift.
Recommended action
Set imageTagMutability=IMMUTABLE on the repository. Reference images by digest (sha256:...) in deployment manifests for strongest immutability guarantees.
ECR-003: Repository policy allows public access
A wildcard-principal repo policy means anyone on the internet can pull images. Sometimes intentional (a publicly-distributed base image), but should be a deliberate exposure, typically via the ECR Public registry rather than a private repo with a public policy. The default for build-output images should never be public.
Recommended action
Remove wildcard principals from the repository policy. Grant access only to specific AWS account IDs or IAM principals that require it.
ECR-004: No lifecycle policy configured
Without a lifecycle policy, untagged images and old tagged images accumulate indefinitely. Stale images keep CVE attack surface available, anyone who can pull from the repo can pull the old, unpatched version even after a newer build has shipped. Lifecycle expiry is the housekeeper that closes that window.
Recommended action
Add a lifecycle policy that expires untagged images after a short period (e.g. 7 days) and limits the number of tagged images retained, reducing exposure to images with known CVEs.
ECR-005: Repository encrypted with AES256 rather than KMS CMK
Same shape as CP-002 / CWL-002 / CCM-002: AES256 (the AWS-managed default) gives confidentiality at rest but no key-policy or CloudTrail Decrypt-event story. Container images are arguably sensitive intellectual property, the same key-policy + audit shape as build outputs in S3 is warranted.
Recommended action
Set encryptionType=KMS with a customer-managed key ARN.
ECR-006: ECR pull-through cache rule uses an untrusted upstream
AWS supports pull-through cache for ECR Public, Quay, K8s, GitHub Container Registry, GitLab, and Docker Hub. A rule pointing at registry-1.docker.io without an authenticated credential silently caches whatever the public namespace resolves to.
Recommended action
Scope pull-through cache rules to AWS-trusted registries (ECR Public, Quay.io with authentication, or a vetted private registry). Avoid wildcard or unauthenticated upstreams, a malicious image there gets cached into your account registry on first pull.
ECR-007: Inspector v2 enhanced scanning disabled for ECR
ECR-001's basic on-push scan covers OS-level packages, apt / yum / apk lineage. Most production CVE risk is in language ecosystems (npm, pip, gem, mvn) which the basic scanner ignores. Inspector v2 enhanced scanning closes that gap and runs continuously, so a CVE published two weeks after a build still surfaces against the deployed image.
Recommended action
Enable Amazon Inspector v2 for the ECR scan type on this account. Basic ECR scanning on-push only covers OS packages; Inspector v2 enhanced scanning adds language-ecosystem CVEs and runs continuously as new vulnerabilities are published.
IAM-001: CI/CD role has AdministratorAccess policy attached
A CI/CD service role with AdministratorAccess attached turns any pipeline compromise into account compromise. The classic anti-pattern: the role started narrow, the pipeline grew, someone attached AdministratorAccess to unblock a deploy, and it never came off.
Recommended action
Replace AdministratorAccess with least-privilege policies.
IAM-002: CI/CD role has wildcard Action in attached policy
Action: '*' on an attached policy is functionally equivalent to AdministratorAccess: the role can call every API in every service. The wildcard absorbs every new IAM action AWS adds, so the role's authority grows without any local change. Service-prefix wildcards like s3:* are caught by IAM-006, not this rule.
Recommended action
Replace wildcard actions with specific IAM actions.
IAM-003: CI/CD role has no permission boundary
A permissions boundary is the maximum-permission ceiling for a role. Without one, every future PR that attaches another inline / managed policy raises the role's effective authority indefinitely. With a boundary in place, the policy churn happens beneath a fixed cap that your security team owns separately.
Recommended action
Attach a permissions boundary defining max permissions.
IAM-004: CI/CD role can PassRole to any role
iam:PassRole with Resource: '*' lets the principal hand any role to any service. Combined with a service that runs your code (Lambda, ECS, CodeBuild, EC2 Instance Profiles), this is role-hop privilege escalation: launch an ephemeral resource configured with a higher-privileged role, run code under that identity, exfil. Scoping by ARN + iam:PassedToService removes the escalation path.
Recommended action
Restrict iam:PassRole to specific role ARNs and add an iam:PassedToService condition.
IAM-005: CI/CD role trust policy missing sts:ExternalId
A trust policy that lets an external AWS account assume the role without an sts:ExternalId condition is vulnerable to the confused-deputy pattern: a third-party SaaS configured with your role ARN can also be used by another customer of that SaaS to assume your role (if they know the ARN). sts:ExternalId ties the role to a specific tenancy.
Recommended action
Add a Condition requiring sts:ExternalId for external principals.
IAM-006: Sensitive actions granted with wildcard Resource
IAM-002 catches Action: "*". IAM-006 catches the more common "scoped action, unscoped resource" pattern on sensitive services (S3/KMS/SecretsManager/SSM/IAM/STS/DynamoDB/Lambda/EC2).
Recommended action
Scope the Resource element to specific ARNs (buckets, keys, secrets, roles).
IAM-007: IAM user has access key older than 90 days
Every user in the account is evaluated. CI/CD tooling that still uses IAM users (older Jenkins agents, GitHub Actions pre-OIDC, third-party schedulers) shows up here. The 90-day window matches the common compliance baseline; rotate sooner if the key is used from on-prem or an untrusted runner.
Recommended action
Rotate or delete IAM access keys older than 90 days. Long-lived static credentials are the #1 way compromised CI credentials get reused across environments, prefer short-lived STS tokens via OIDC federation or an assumed role.
IAM-008: OIDC-federated role trust policy missing audience or subject pin
IAM-005 already covers cross-account AWS principals. This rule targets the OIDC federation path specifically because the blast radius of a missed audience/subject pin is the entire identity provider's tenant base (e.g. all GitHub users, not just your org). For GitHub repo: subjects it also fires when the subject is present but wildcards the repo or ref segment, or trusts the pull_request context, since a fork PR then mints the role's token.
Recommended action
Every Allow statement that trusts a federated OIDC provider (token.actions.githubusercontent.com, GitLab, CircleCI, Terraform Cloud, etc.) must pin both the audience (...:aud = sts.amazonaws.com) and a specific subject (...:sub matching one repo AND ref, e.g. repo:myorg/myrepo:ref:refs/heads/main or ...:environment:production). An org wildcard (repo:myorg/*), a ref wildcard (repo:myorg/myrepo:*), or the pull_request context all let an untrusted workflow run (including a fork PR) assume the role.
KMS-001: KMS customer-managed key has rotation disabled
Annual rotation regenerates the underlying key material for the same CMK ARN. Existing ciphertexts can still be decrypted (KMS keeps old material around), but new encrypts use the new material, so a cryptographic exposure (side-channel, an accidental export, an old compromised offline backup) only protects ciphertexts from before the rotation.
Recommended action
Enable annual rotation on every customer-managed KMS key used for CI/CD artifact, log, and secret encryption. Unrotated CMKs keep the same key material indefinitely, so a single cryptographic exposure (side-channel, accidental export) is permanent.
KMS-002: KMS key policy grants wildcard KMS actions
kms:* on a key policy is administrative authority over the cipher boundary: CancelKeyDeletion, ScheduleKeyDeletion, ReEncrypt, UpdateKeyDescription, and the data-plane decrypt actions all collapse into one grant. A CI/CD principal almost never needs more than the data-plane subset (Decrypt / GenerateDataKey / Encrypt).
Recommended action
Replace kms:* grants with specific actions needed by the caller (e.g. kms:Decrypt, kms:GenerateDataKey). Key-policy wildcard grants let any holder of the principal re-key, schedule deletion, or export material at will.
LMB-001: Lambda function has no code-signing config
Lambda code-signing config + a Signer profile (SIGN-001) validates that an uploaded zip was signed by a known profile before it's allowed to run. Without one, anyone who reaches lambda:UpdateFunctionCode, a CI/CD role compromise, a misattached IAM policy, can replace the function's code with no chain-of-custody check.
Recommended action
Create an AWS Signer profile, reference it from an aws_lambda_code_signing_config with untrusted_artifact_on_deployment = Enforce and attach that config to the function. Without one, the Lambda runtime will execute any code that a principal with lambda:UpdateFunctionCode uploads.
LMB-002: Lambda function URL has AuthType=NONE
A Lambda function URL with AuthType=NONE is a public HTTPS endpoint. Anyone who knows the URL can invoke. This is sometimes deliberate (a webhook receiver) but the deliberate version typically signs / validates inside the function, the rule fires regardless because the IAM-side control isn't there.
Recommended action
Set the function URL auth_type to AWS_IAM and grant lambda:InvokeFunctionUrl through IAM. NONE exposes the function to the public internet without authentication.
LMB-003: Lambda function env vars may contain plaintext secrets
Lambda env vars are world-readable to any principal with lambda:GetFunctionConfiguration, much wider than the principal that can invoke the function. They also persist in CloudFormation drift, change-sets, and CloudTrail events. A secret in a Lambda env var is essentially exposed to anyone with read access to the account.
Recommended action
Move secrets out of Lambda environment variables and into Secrets Manager or SSM Parameter Store. Environment variables are visible to anyone with lambda:GetFunctionConfiguration and persist in CloudTrail events, which keeps the secret in audit logs.
LMB-004: Lambda resource policy allows wildcard principal
A wildcard-principal Allow on a Lambda function resource policy lets anyone invoke. The legitimate case is a service principal (API Gateway, S3 events) where AWS fills in the SourceArn/SourceAccount at invoke time, without those conditions, any account using that service can invoke.
Recommended action
Remove Allow statements with Principal: '*' from every Lambda function resource policy, or scope them with a SourceArn / SourceAccount condition. Service principals (e.g. apigateway.amazonaws.com) are the common legitimate case, ensure they carry a condition.
PBAC-001: CodeBuild project has no VPC configuration
A CodeBuild project with no VPC configuration runs in AWS-managed network space, egress to the public internet is unrestricted, every package registry / CDN / arbitrary endpoint is reachable. Inside a VPC, security-group + VPC-endpoint policies become the egress gate, which is the only practical way to limit a compromised build's exfiltration paths.
Recommended action
Configure the CodeBuild project to run inside a VPC with appropriate subnets and security groups. Use a NAT gateway or VPC endpoints to control outbound internet access and restrict build nodes to only the network resources they require.
PBAC-002: CodeBuild service role shared across multiple projects
One CodeBuild service role across many projects means a compromise of any project's build environment grants access to whatever resources every other project's build needs. Per-project roles cap the radius, a backdoor in the foo-tests build can't reach the deploy-prod build's secrets if they each have their own role.
Recommended action
Create a dedicated IAM service role for each CodeBuild project, scoped to only the permissions that specific project requires. This limits the blast radius if one project's build is compromised.
PBAC-003: CodeBuild security group allows 0.0.0.0/0 all-port egress
A security-group egress rule of 0.0.0.0/0 on all ports/protocols means a compromised build can connect to any endpoint on the internet, typosquat-package registry, C2 server, attacker-owned dump endpoint. Even when the build is inside a VPC (PBAC-001), this egress rule negates the network-side gating.
Recommended action
Restrict CodeBuild security-group egress to the specific endpoints builds need (package registries, artifact repositories, STS). A wildcard egress rule lets a compromised build exfiltrate to anywhere on the internet.
PBAC-005: CodePipeline stage action roles mirror the pipeline role
When stage actions don't set their own roleArn, they fall back to the pipeline-level role, which is the union of every stage's needs. A compromise of any one stage (typically the build, which runs untrusted code) gains the deploy stage's authority, including production deploy credentials. Per-action roles cap the radius.
Recommended action
Give each stage action (Source, Build, Deploy) its own narrowly-scoped IAM role via roleArn on the action declaration. Sharing the pipeline-level role means a compromise of one action (e.g. a build) gains the permissions the deploy stage also needs.
S3-001: Artifact bucket public access block not fully enabled
S3 Block Public Access is the bucket-level circuit breaker that supersedes any future ACL or bucket-policy edit. Without all four settings enabled, a misconfigured CloudFormation change or a stray aws s3api call can re-expose the bucket to the public, even if the bucket had previously been private.
Recommended action
Enable all four S3 Block Public Access settings on the artifact bucket: BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, and RestrictPublicBuckets.
S3-002: Artifact bucket server-side encryption not configured
Default bucket encryption applies SSE-S3 (AES256) to every PutObject. As of January 2023, AWS enables this on all new buckets automatically, but existing buckets created before then can still be unencrypted unless explicitly configured. Without it, individual objects can be uploaded without encryption (the client gets to choose).
Recommended action
Enable default bucket encryption using at minimum AES256 (SSE-S3). For stronger key control, use SSE-KMS with a customer-managed key.
S3-003: Artifact bucket versioning not enabled
Versioning makes overwrites and deletes recoverable: the previous content of an object survives until lifecycle expires it. Without versioning, an artifact overwrite (a bad pipeline run, a malicious replacement, a typo'd aws s3 cp) is unrecoverable, the original bytes are gone.
Recommended action
Enable S3 versioning on the artifact bucket so that previous artifact versions are retained and rollback is possible. Combine with a lifecycle rule to expire old versions after a retention period.
S3-004: Artifact bucket access logging not enabled
S3 server access logging records every API operation against the bucket, who, when, what object, what method. CloudTrail data events overlap but cost more; access logs are the cheap baseline. Without them, an exfiltration via GetObject doesn't leave a trail you can investigate.
Recommended action
Enable S3 server access logging for the artifact bucket and direct logs to a separate, centralized logging bucket with restricted write access.
S3-005: Artifact bucket missing aws:SecureTransport deny
S3 endpoints accept HTTP and HTTPS by default. Without an explicit Deny on aws:SecureTransport=false, a plaintext request, typically from a misconfigured client or a SDK with a stale endpoint, is honored if signed. The bucket policy Deny is the only enforcement; no account-level switch covers it.
Recommended action
Add a Deny statement for s3:* with Bool aws:SecureTransport=false.
SIGN-001: No AWS Signer profile defined for Lambda deploys
AWS Signer profiles are the upstream of LMB-001's code-signing config. Without a profile defined, no function in the account can enforce code-signing, LMB-001's recommendation has nothing to point at. The profile is the foundation; the per-function code-signing config attaches it.
Recommended action
Create an AWS Signer profile with platform AWSLambda-SHA384-ECDSA and reference it from every Lambda code-signing config used by the pipeline. Without a profile, LMB-001 remediation isn't possible and release artifacts can't be signed at build time.
SIGN-002: AWS Signer profile is revoked or inactive
A revoked or canceled Signer profile invalidates every signature it ever produced. Lambda functions configured to enforce code-signing fail to deploy until the profile is replaced (or, if UntrustedArtifactOnDeployment = Warn, deploy with a CloudWatch warning the operator rarely reads).
Recommended action
Rotate the signing profile: create a replacement and update every code-signing config that references the revoked profile. A revoked or canceled profile invalidates every signature it produced, lambdas relying on it will fail verification.
SM-001: Secrets Manager secret has no rotation configured
Only secrets actually referenced by CodeBuild are checked, secrets used purely by application workloads are out of scope for a CI/CD scanner.
Recommended action
Enable automatic rotation on every Secrets Manager secret referenced by a CodeBuild project or CodePipeline. Unrotated secrets persist indefinitely, so a single leak (e.g. a build log that echoed the value) compromises the secret for its full lifetime.
SM-002: Secrets Manager resource policy allows wildcard principal
A wildcard-principal Allow on a Secrets Manager resource policy means any principal in any AWS account can call GetSecretValue (subject to conditions, if any). Always combine with at least aws:SourceAccount or aws:PrincipalOrgID, the lift-and-shift cross-account secret-access pattern needs scoping.
Recommended action
Remove Allow statements whose Principal is * from every Secrets Manager resource policy, or scope them with a Condition restricting the source account/org (aws:PrincipalOrgID). A wildcard-principal policy allows any AWS account to call GetSecretValue on the secret.
SSM-001: SSM Parameter with secret-like name is not a SecureString
An SSM String parameter is plaintext at rest and at API; ssm:GetParameter without any KMS Decrypt authority returns the value. SecureString adds KMS-encryption + the WithDecryption=true flag (which forces an explicit KMS authorization step). Secret-named parameters (TOKEN, PASSWORD, KEY) are almost always intended to be SecureString and rarely should not be.
Recommended action
Recreate the parameter with Type=SecureString and migrate consumers to the new name if needed. Plain String parameters are visible via ssm:GetParameter without any KMS authorization.
SSM-002: SSM SecureString uses the default AWS-managed key
alias/aws/ssm is the AWS-managed default for SecureString. Its key policy is fixed and account-wide. A customer-managed key gives you the same per-parameter key-policy + CloudTrail audit story you'd apply to Secrets Manager (which always uses a CMK).
Recommended action
Recreate SecureString parameters with KeyId pointing at a customer-managed KMS key. The default alias/aws/ssm key is shared across the account and its key policy cannot be audited or scoped per parameter.
Adding a new AWS check
- Drop a single module in
pipeline_check/core/checks/aws/rules/<id>_<slug>.pyexporting aRULE(metadata) and acheck(catalog: ResourceCatalog) -> list[Finding]callable. The orchestrator (AWSRuleChecks) auto-discovers it and this doc's table picks it up on the next regen. - If the check needs a new enumeration, add a cached method to
ResourceCataloginpipeline_check/core/checks/aws/_catalog.pyso every dependent rule reads from the same in-memory snapshot. - Add the check ID to
pipeline_check/core/standards/data/owasp_cicd_top_10.py(and any other standard that applies). - Add unit tests in
tests/aws/rules/test_<name>.pyusing themake_catalogfixture. - (Recommended) Add a Terraform parity rule in
pipeline_check/core/checks/terraform/{extended,services,phase3}.pyso shift-left scans stay at parity with the runtime provider. - Regenerate this doc: