CIS AWS Foundations Benchmark
- Version: 5.0
- URL: https://www.cisecurity.org/benchmark/amazon_web_services
- Source of truth:
pipeline_check/core/standards/data/cis_aws_foundations.py
CIS AWS Foundations Benchmark, CI/CD-relevant subset. IAM hardening, S3 protection, KMS hygiene, and the CloudTrail / CloudWatch logging controls the AWS provider scans against a live account.
At a glance
- Controls in this standard: 14
- Controls evidenced by at least one check: 12 / 14
- Distinct checks evidencing this standard: 40
- Of those, autofixable with
--fix: 0
How to read severity
Every check below ships at a fixed severity level. The scale is the same across providers and standards so a CRITICAL finding in one place means the same thing as a CRITICAL finding anywhere else.
| Level | What it means | Examples |
|---|---|---|
| CRITICAL | Active exploit primitive in the workflow as written. Treat as P0: a default scan path lands an attacker on a secret, an RCE, or production write access without further effort. | Hardcoded credential literal, branch ref pointing at a known-compromised action, signed-into-an-unverified registry. |
| HIGH | Production-impact gap that requires modest attacker effort or a second condition to weaponize. Remediate this sprint; the secondary condition is usually already present in real pipelines. | Action pinned to a floating tag, sensitive permissions on a low-popularity action, mutable container tag in prod. |
| MEDIUM | Significant defense-in-depth gap. Not directly exploitable on its own but disables a control whose absence widens the blast radius of a separate compromise. Backlog with a deadline. | Missing branch protection, container without resource limits, freshly-published dependency consumed before the cooldown window. |
| LOW | Hygiene / hardening issue. Not a vulnerability on its own but raises baseline posture and reduces audit friction. | Missing CI logging retention, SBOM without supplier attribution, ECR repo without scan-on-push. |
| INFO | Degraded-mode signal. The scanner couldn't reach an API or parse a config and surfaces the gap so the operator knows coverage was incomplete. No finding against the workload itself. | CB-000 CodeBuild API access failed, IAM-000 IAM enumeration failed. |
Coverage by control
Click a control ID to jump to the per-control section with the full check list. The severity mix column shows the spread of evidencing checks by severity (Critical / High / Medium / Low / Info).
| Control | Title | Checks | Severity mix |
|---|---|---|---|
1.14 |
Ensure access keys are rotated every 90 days or less | 2 | 2H |
1.16 |
Ensure IAM policies that allow full ':' administrative privileges are not attached | 16 | 5C · 8H · 3M |
1.17 |
Ensure a support role has been created to manage incidents with AWS Support | 0 | — |
2.1.1 |
Ensure all S3 buckets employ encryption-at-rest | 1 | 1H |
2.1.2 |
Ensure S3 Bucket Policy is set to deny HTTP requests | 2 | 2M |
2.1.4 |
Ensure that S3 Buckets are configured with 'Block public access' | 1 | 1C |
3.1 |
Ensure CloudTrail is enabled in all regions | 2 | 1H · 1M |
3.2 |
Ensure CloudTrail log file validation is enabled | 1 | 1M |
3.4 |
Ensure CloudTrail trails are integrated with CloudWatch Logs | 3 | 2M · 1L |
3.6 |
Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket | 1 | 1L |
3.7 |
Ensure CloudTrail logs are encrypted at rest using KMS CMKs | 8 | 2H · 6M |
3.8 |
Ensure rotation for customer-created symmetric CMKs is enabled | 2 | 2M |
4.3 |
Ensure a log metric filter and alarm exist for usage of the root account | 0 | — |
4.16 |
Ensure AWS Security Hub is enabled | 2 | 1H · 1M |
Filter at runtime
Restrict a scan to checks that evidence this standard with --standard cis_aws_foundations:
# All providers, only checks tied to this standard
pipeline_check --standard cis_aws_foundations
# Compose with --pipeline to scope by provider
pipeline_check --pipeline github --standard cis_aws_foundations
# Compose with another standard to widen the lens
pipeline_check --pipeline aws --standard cis_aws_foundations --standard owasp_cicd_top_10
Controls in scope
1.14: Ensure access keys are rotated every 90 days or less
Evidenced by 2 checks across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
IAM-007 |
IAM user has access key older than 90 days | HIGH | AWS | |
SM-001 |
Secrets Manager secret has no rotation configured | HIGH | AWS |
1.16: Ensure IAM policies that allow full ':' administrative privileges are not attached
Evidenced by 16 checks across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
CA-003 |
CodeArtifact domain policy allows cross-account wildcard | CRITICAL | AWS | |
CA-004 |
CodeArtifact repo policy grants codeartifact: with Resource '' | HIGH | AWS | |
ECR-003 |
Repository policy allows public access | CRITICAL | AWS | |
IAM-001 |
CI/CD role has AdministratorAccess policy attached | CRITICAL | AWS | |
IAM-002 |
CI/CD role has wildcard Action in attached policy | HIGH | AWS | |
IAM-003 |
CI/CD role has no permission boundary | MEDIUM | AWS | |
IAM-004 |
CI/CD role can PassRole to any role | HIGH | AWS | |
IAM-005 |
CI/CD role trust policy missing sts:ExternalId | HIGH | AWS | |
IAM-006 |
Sensitive actions granted with wildcard Resource | MEDIUM | AWS | |
IAM-008 |
OIDC-federated role trust policy missing audience or subject pin | HIGH | AWS | |
KMS-002 |
KMS key policy grants wildcard KMS actions | HIGH | AWS | |
LMB-002 |
Lambda function URL has AuthType=NONE | HIGH | AWS | |
LMB-004 |
Lambda resource policy allows wildcard principal | CRITICAL | AWS | |
PBAC-002 |
CodeBuild service role shared across multiple projects | MEDIUM | AWS | |
PBAC-005 |
CodePipeline stage action roles mirror the pipeline role | HIGH | AWS | |
SM-002 |
Secrets Manager resource policy allows wildcard principal | CRITICAL | AWS |
1.17: Ensure a support role has been created to manage incidents with AWS Support
No checks in this scanner currently evidence this control. Open an issue if your team would value coverage.
2.1.1: Ensure all S3 buckets employ encryption-at-rest
Evidenced by 1 check across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
S3-002 |
Artifact bucket server-side encryption not configured | HIGH | AWS |
2.1.2: Ensure S3 Bucket Policy is set to deny HTTP requests
Evidenced by 2 checks across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
S3-003 |
Artifact bucket versioning not enabled | MEDIUM | AWS | |
S3-005 |
Artifact bucket missing aws:SecureTransport deny | MEDIUM | AWS |
2.1.4: Ensure that S3 Buckets are configured with 'Block public access'
Evidenced by 1 check across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
S3-001 |
Artifact bucket public access block not fully enabled | CRITICAL | AWS |
3.1: Ensure CloudTrail is enabled in all regions
Evidenced by 2 checks across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
CT-001 |
No active CloudTrail trail in region | HIGH | AWS | |
CT-003 |
CloudTrail trail is not multi-region | MEDIUM | AWS |
3.2: Ensure CloudTrail log file validation is enabled
Evidenced by 1 check across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
CT-002 |
CloudTrail log-file validation disabled | MEDIUM | AWS |
3.4: Ensure CloudTrail trails are integrated with CloudWatch Logs
Evidenced by 3 checks across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
CB-003 |
Build logging not enabled | MEDIUM | AWS | |
CD-003 |
No CloudWatch alarm monitoring on deployment group | MEDIUM | AWS | |
CWL-001 |
CodeBuild log group has no retention policy | LOW | AWS |
3.6: Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket
Evidenced by 1 check across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
S3-004 |
Artifact bucket access logging not enabled | LOW | AWS |
3.7: Ensure CloudTrail logs are encrypted at rest using KMS CMKs
Evidenced by 8 checks across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
CA-001 |
CodeArtifact domain not encrypted with customer KMS CMK | MEDIUM | AWS | |
CCM-002 |
CodeCommit repository not encrypted with customer KMS CMK | MEDIUM | AWS | |
CP-002 |
Artifact store not encrypted with customer-managed KMS key | MEDIUM | AWS | |
CWL-002 |
CodeBuild log group not KMS-encrypted | MEDIUM | AWS | |
ECR-005 |
Repository encrypted with AES256 rather than KMS CMK | MEDIUM | AWS | |
LMB-003 |
Lambda function env vars may contain plaintext secrets | HIGH | AWS | |
SSM-001 |
SSM Parameter with secret-like name is not a SecureString | HIGH | AWS | |
SSM-002 |
SSM SecureString uses the default AWS-managed key | MEDIUM | AWS |
3.8: Ensure rotation for customer-created symmetric CMKs is enabled
Evidenced by 2 checks across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
KMS-001 |
KMS customer-managed key has rotation disabled | MEDIUM | AWS | |
SSM-002 |
SSM SecureString uses the default AWS-managed key | MEDIUM | AWS |
4.3: Ensure a log metric filter and alarm exist for usage of the root account
No checks in this scanner currently evidence this control. Open an issue if your team would value coverage.
4.16: Ensure AWS Security Hub is enabled
Evidenced by 2 checks across AWS.
| Check | Title | Severity | Provider | Fix |
|---|---|---|---|---|
ECR-001 |
Image scanning on push not enabled | HIGH | AWS | |
ECR-007 |
Inspector v2 enhanced scanning disabled for ECR | MEDIUM | AWS |
Check details
Every check that evidences this standard, rendered once with its detection mechanism, recommendation, and any known false-positive modes or real-world incident references. The per-control tables above link to the matching block here.
CA-001: CodeArtifact domain not encrypted with customer KMS CMK MEDIUM
Evidences: 3.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs.
How this is detected. AWS-owned encryption (the default alias/aws/codeartifact key) keeps the key policy under AWS's control, not yours. That's fine for confidentiality but means cross-account auditability of every Decrypt event lives with AWS, and you can't revoke or scope key access without recreating the domain. A customer-managed CMK puts both controls back in your hands.
Recommendation. 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.
Source: CA-001 in the AWS provider.
CA-003: CodeArtifact domain policy allows cross-account wildcard CRITICAL
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. 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.
Source: CA-003 in the AWS provider.
CA-004: CodeArtifact repo policy grants codeartifact: with Resource '' HIGH
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. 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.
Source: CA-004 in the AWS provider.
CB-003: Build logging not enabled MEDIUM
Evidences: 3.4 Ensure CloudTrail trails are integrated with CloudWatch Logs.
How this is detected. 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.
Recommendation. Enable CloudWatch Logs or S3 logging in the CodeBuild project configuration to maintain a durable audit trail of all build activity.
Source: CB-003 in the AWS provider.
CCM-002: CodeCommit repository not encrypted with customer KMS CMK MEDIUM
Evidences: 3.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs.
How this is detected. 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.
Recommendation. 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.
Source: CCM-002 in the AWS provider.
CD-003: No CloudWatch alarm monitoring on deployment group MEDIUM
Evidences: 3.4 Ensure CloudTrail trails are integrated with CloudWatch Logs.
How this is detected. 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.
Recommendation. 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.
Source: CD-003 in the AWS provider.
CP-002: Artifact store not encrypted with customer-managed KMS key MEDIUM
Evidences: 3.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs.
How this is detected. 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.
Recommendation. 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.
Source: CP-002 in the AWS provider.
CT-001: No active CloudTrail trail in region HIGH
Evidences: 3.1 Ensure CloudTrail is enabled in all regions.
How this is detected. 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.
Recommendation. 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.
Source: CT-001 in the AWS provider.
CT-002: CloudTrail log-file validation disabled MEDIUM
Evidences: 3.2 Ensure CloudTrail log file validation is enabled.
How this is detected. 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/.
Recommendation. 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.
Source: CT-002 in the AWS provider.
CT-003: CloudTrail trail is not multi-region MEDIUM
Evidences: 3.1 Ensure CloudTrail is enabled in all regions.
How this is detected. 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.
Recommendation. 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.
Source: CT-003 in the AWS provider.
CWL-001: CodeBuild log group has no retention policy LOW
Evidences: 3.4 Ensure CloudTrail trails are integrated with CloudWatch Logs.
How this is detected. 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.
Recommendation. 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.
Source: CWL-001 in the AWS provider.
CWL-002: CodeBuild log group not KMS-encrypted MEDIUM
Evidences: 3.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs.
How this is detected. 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.
Recommendation. 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.
Source: CWL-002 in the AWS provider.
ECR-001: Image scanning on push not enabled HIGH
Evidences: 4.16 Ensure AWS Security Hub is enabled.
How this is detected. 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).
Recommendation. Enable imageScanningConfiguration.scanOnPush on the repository. Consider also enabling Amazon Inspector continuous scanning for ongoing CVE detection against images already in the registry.
Source: ECR-001 in the AWS provider.
ECR-003: Repository policy allows public access CRITICAL
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. Remove wildcard principals from the repository policy. Grant access only to specific AWS account IDs or IAM principals that require it.
Source: ECR-003 in the AWS provider.
ECR-005: Repository encrypted with AES256 rather than KMS CMK MEDIUM
Evidences: 3.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs.
How this is detected. 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.
Recommendation. Set encryptionType=KMS with a customer-managed key ARN.
Source: ECR-005 in the AWS provider.
ECR-007: Inspector v2 enhanced scanning disabled for ECR MEDIUM
Evidences: 4.16 Ensure AWS Security Hub is enabled.
How this is detected. 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.
Recommendation. 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.
Source: ECR-007 in the AWS provider.
IAM-001: CI/CD role has AdministratorAccess policy attached CRITICAL
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. Replace AdministratorAccess with least-privilege policies.
Source: IAM-001 in the AWS provider.
IAM-002: CI/CD role has wildcard Action in attached policy HIGH
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. Action: '*' (or service-prefix wildcards like s3:*) on an attached policy is functionally equivalent to AdministratorAccess for that resource. The wildcard absorbs every new IAM action AWS adds, so the role's authority grows without any local change.
Recommendation. Replace wildcard actions with specific IAM actions.
Source: IAM-002 in the AWS provider.
IAM-003: CI/CD role has no permission boundary MEDIUM
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. Attach a permissions boundary defining max permissions.
Source: IAM-003 in the AWS provider.
IAM-004: CI/CD role can PassRole to any role HIGH
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. Restrict iam:PassRole to specific role ARNs and add an iam:PassedToService condition.
Source: IAM-004 in the AWS provider.
IAM-005: CI/CD role trust policy missing sts:ExternalId HIGH
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. Add a Condition requiring sts:ExternalId for external principals.
Source: IAM-005 in the AWS provider.
IAM-006: Sensitive actions granted with wildcard Resource MEDIUM
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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).
Recommendation. Scope the Resource element to specific ARNs (buckets, keys, secrets, roles).
Source: IAM-006 in the AWS provider.
IAM-007: IAM user has access key older than 90 days HIGH
Evidences: 1.14 Ensure access keys are rotated every 90 days or less.
How this is detected. 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.
Recommendation. 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.
Source: IAM-007 in the AWS provider.
IAM-008: OIDC-federated role trust policy missing audience or subject pin HIGH
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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).
Recommendation. 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 subject prefix (...:sub matching repo:myorg/*). Without these, any workflow from any tenant can assume the role.
Source: IAM-008 in the AWS provider.
KMS-001: KMS customer-managed key has rotation disabled MEDIUM
Evidences: 3.8 Ensure rotation for customer-created symmetric CMKs is enabled.
How this is detected. 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.
Recommendation. 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.
Source: KMS-001 in the AWS provider.
KMS-002: KMS key policy grants wildcard KMS actions HIGH
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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).
Recommendation. 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.
Source: KMS-002 in the AWS provider.
LMB-002: Lambda function URL has AuthType=NONE HIGH
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. 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.
Source: LMB-002 in the AWS provider.
LMB-003: Lambda function env vars may contain plaintext secrets HIGH
Evidences: 3.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs.
How this is detected. 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.
Recommendation. 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.
Source: LMB-003 in the AWS provider.
LMB-004: Lambda resource policy allows wildcard principal CRITICAL
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. 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.
Source: LMB-004 in the AWS provider.
PBAC-002: CodeBuild service role shared across multiple projects MEDIUM
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. 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.
Source: PBAC-002 in the AWS provider.
PBAC-005: CodePipeline stage action roles mirror the pipeline role HIGH
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. 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.
Source: PBAC-005 in the AWS provider.
S3-001: Artifact bucket public access block not fully enabled CRITICAL
Evidences: 2.1.4 Ensure that S3 Buckets are configured with 'Block public access'.
How this is detected. 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.
Recommendation. Enable all four S3 Block Public Access settings on the artifact bucket: BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, and RestrictPublicBuckets.
Source: S3-001 in the AWS provider.
S3-002: Artifact bucket server-side encryption not configured HIGH
Evidences: 2.1.1 Ensure all S3 buckets employ encryption-at-rest.
How this is detected. 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).
Recommendation. Enable default bucket encryption using at minimum AES256 (SSE-S3). For stronger key control, use SSE-KMS with a customer-managed key.
Source: S3-002 in the AWS provider.
S3-003: Artifact bucket versioning not enabled MEDIUM
Evidences: 2.1.2 Ensure S3 Bucket Policy is set to deny HTTP requests.
How this is detected. 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.
Recommendation. 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.
Source: S3-003 in the AWS provider.
S3-004: Artifact bucket access logging not enabled LOW
Evidences: 3.6 Ensure S3 bucket access logging is enabled on the CloudTrail S3 bucket.
How this is detected. 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.
Recommendation. Enable S3 server access logging for the artifact bucket and direct logs to a separate, centralized logging bucket with restricted write access.
Source: S3-004 in the AWS provider.
S3-005: Artifact bucket missing aws:SecureTransport deny MEDIUM
Evidences: 2.1.2 Ensure S3 Bucket Policy is set to deny HTTP requests.
How this is detected. 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.
Recommendation. Add a Deny statement for s3:* with Bool aws:SecureTransport=false.
Source: S3-005 in the AWS provider.
SM-001: Secrets Manager secret has no rotation configured HIGH
Evidences: 1.14 Ensure access keys are rotated every 90 days or less.
How this is detected. Only secrets actually referenced by CodeBuild are checked, secrets used purely by application workloads are out of scope for a CI/CD scanner.
Recommendation. 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.
Source: SM-001 in the AWS provider.
SM-002: Secrets Manager resource policy allows wildcard principal CRITICAL
Evidences: 1.16 Ensure IAM policies that allow full ':' administrative privileges are not attached.
How this is detected. 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.
Recommendation. 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.
Source: SM-002 in the AWS provider.
SSM-001: SSM Parameter with secret-like name is not a SecureString HIGH
Evidences: 3.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs.
How this is detected. 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.
Recommendation. 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.
Source: SSM-001 in the AWS provider.
SSM-002: SSM SecureString uses the default AWS-managed key MEDIUM
Evidences: 3.7 Ensure CloudTrail logs are encrypted at rest using KMS CMKs, 3.8 Ensure rotation for customer-created symmetric CMKs is enabled.
How this is detected. 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).
Recommendation. 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.
Source: SSM-002 in the AWS provider.
This page is generated. Edit pipeline_check/core/standards/data/cis_aws_foundations.py (mappings) or scripts/gen_standards_docs.py (intro / per-control prose) and run python scripts/gen_standards_docs.py cis_aws_foundations.