You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
sec(ci): main is unprotected while ref:refs/heads/main is a trusted OIDC subject for the AWS deploy role #154
main has no required-review protection, while repo:LeanerCloud/CUDly:ref:refs/heads/main is a trusted OIDC subject for the cudly-terraform-deploy AWS role. Anyone with write access can therefore push a workflow edit directly to main and mint AWS deploy credentials, with no review and no human gate anywhere in the chain.
This is the exposure that makes LeanerCloud/cloud-commitments-cli#1649's release-tag injection collapse in severity: the tag path grants strictly less than an actor with write access already holds. Recording it separately because it is larger than the bug LeanerCloud/cloud-commitments-cli#1657 fixes, and it is not addressed by any open PR.
Evidence
1. main is not protected. Classic branch protection:
$ gh api repos/LeanerCloud/CUDly/branches/main/protection
{"message":"Branch not protected", "status":"404"}
Checked with a token reporting "admin": true on the repo, so this is a genuine 404 and not an insufficient-scope artifact.
2. The one active ruleset does not require review.protect-master-branch is enforcement: active, but the rules it actually contributes to main are:
$ gh api repos/LeanerCloud/CUDly/rules/branches/main --jq '[.[]|.type]'
["deletion","non_fast_forward"]
That blocks deleting main and force-pushing to it. It does not require a pull request, an approving review, or status checks. The ruleset's name suggests it may have been written for a master branch that no longer exists.
3. No CODEOWNERS. No CODEOWNERS file at the repo root, in .github/, or in docs/. Nothing gives .github/workflows/** special review treatment.
4. main is a trusted OIDC subject.terraform/environments/aws/ci-cd-permissions/role.tf on main (cd4ee031f, after LeanerCloud/cloud-commitments-cli#1683) lists 16 subjects. The first is:
"repo:${var.github_repo}:ref:refs/heads/main",
The remaining 15 are environment:* subjects covering the deploy, DB-migration, fargate and rollback environment families. Only the ref:refs/heads/main entry is relevant here: a workflow job running on a push to mainwithout an environment: binding presents sub = repo:LeanerCloud/CUDly:ref:refs/heads/main and matches it.
Reproduce the full list with:
git show origin/main:terraform/environments/aws/ci-cd-permissions/role.tf \
| grep -n '"repo:${var.github_repo}'
Why it matters
The chain is: write access -> push a workflow to main -> job runs with id-token: write -> OIDC sub ref:refs/heads/main -> assume cudly-terraform-deploy -> full deploy permissions.
No step requires review, approval, or any second party.
Compounding factor (LeanerCloud/cloud-commitments-cli#1648): no GitHub Environment in this repo has required-reviewer protection configured. So the environment: bindings that scope the OIDC subject provide subject scoping but not a human gate. A release auto-deploys to prod with AWS credentials and nothing pauses it.
What this is NOT
Stating the limits explicitly, because several recent p0s in this backlog have had threat models that did not survive scrutiny:
This requires write access to the repository. It is an insider or compromised-credential exposure, not an anonymous or external one.
It is not remote code execution by an unauthenticated party, and not a data-exfiltration path on its own.
A fork pull request does not reach it: fork PRs do not receive id-token: write or repository secrets.
Severity is high rather than critical on that basis: real, unreviewed, and privilege-granting, but gated behind an already-trusted principal.
The honest framing is that this removes the second line of defence rather than the first. It means a single compromised contributor account, or a single bad merge, reaches production AWS with nothing in the way.
Suggested remediation
Not prescribing the exact policy, but the shape:
Require pull requests plus at least one approving review on main (either as a ruleset rule or classic protection). The existing protect-master-branch ruleset appears to be the intended home and is currently near-empty for main.
Add a CODEOWNERS entry for .github/workflows/** and terraform/environments/*/ci-cd-permissions/** so credential-granting configuration needs a named reviewer.
Configure required reviewers on the prod Environment at minimum (this is LeanerCloud/cloud-commitments-cli#1648).
All four evidence points are reproducible read-only:
gh api repos/LeanerCloud/CUDly/branches/main/protection
gh api repos/LeanerCloud/CUDly/rulesets --jq '.[]|{name,target,enforcement}'
gh api repos/LeanerCloud/CUDly/rules/branches/main --jq '[.[]|.type]'
ls CODEOWNERS .github/CODEOWNERS docs/CODEOWNERS
sed -n '20,40p' terraform/environments/aws/ci-cd-permissions/role.tf
Summary
mainhas no required-review protection, whilerepo:LeanerCloud/CUDly:ref:refs/heads/mainis a trusted OIDC subject for thecudly-terraform-deployAWS role. Anyone with write access can therefore push a workflow edit directly tomainand mint AWS deploy credentials, with no review and no human gate anywhere in the chain.This is the exposure that makes LeanerCloud/cloud-commitments-cli#1649's release-tag injection collapse in severity: the tag path grants strictly less than an actor with write access already holds. Recording it separately because it is larger than the bug LeanerCloud/cloud-commitments-cli#1657 fixes, and it is not addressed by any open PR.
Evidence
1.
mainis not protected. Classic branch protection:Checked with a token reporting
"admin": trueon the repo, so this is a genuine 404 and not an insufficient-scope artifact.2. The one active ruleset does not require review.
protect-master-branchisenforcement: active, but the rules it actually contributes tomainare:That blocks deleting
mainand force-pushing to it. It does not require a pull request, an approving review, or status checks. The ruleset's name suggests it may have been written for amasterbranch that no longer exists.3. No CODEOWNERS. No
CODEOWNERSfile at the repo root, in.github/, or indocs/. Nothing gives.github/workflows/**special review treatment.4.
mainis a trusted OIDC subject.terraform/environments/aws/ci-cd-permissions/role.tfonmain(cd4ee031f, after LeanerCloud/cloud-commitments-cli#1683) lists 16 subjects. The first is:"repo:${var.github_repo}:ref:refs/heads/main",The remaining 15 are
environment:*subjects covering the deploy, DB-migration, fargate and rollback environment families. Only theref:refs/heads/mainentry is relevant here: a workflow job running on apushtomainwithout anenvironment:binding presentssub = repo:LeanerCloud/CUDly:ref:refs/heads/mainand matches it.Reproduce the full list with:
Why it matters
The chain is: write access -> push a workflow to
main-> job runs withid-token: write-> OIDC subref:refs/heads/main-> assumecudly-terraform-deploy-> full deploy permissions.No step requires review, approval, or any second party.
Compounding factor (LeanerCloud/cloud-commitments-cli#1648): no GitHub Environment in this repo has required-reviewer protection configured. So the
environment:bindings that scope the OIDC subject provide subject scoping but not a human gate. A release auto-deploys to prod with AWS credentials and nothing pauses it.What this is NOT
Stating the limits explicitly, because several recent p0s in this backlog have had threat models that did not survive scrutiny:
id-token: writeor repository secrets.highrather thancriticalon that basis: real, unreviewed, and privilege-granting, but gated behind an already-trusted principal.The honest framing is that this removes the second line of defence rather than the first. It means a single compromised contributor account, or a single bad merge, reaches production AWS with nothing in the way.
Suggested remediation
Not prescribing the exact policy, but the shape:
main(either as a ruleset rule or classic protection). The existingprotect-master-branchruleset appears to be the intended home and is currently near-empty formain.CODEOWNERSentry for.github/workflows/**andterraform/environments/*/ci-cd-permissions/**so credential-granting configuration needs a named reviewer.prodEnvironment at minimum (this is LeanerCloud/cloud-commitments-cli#1648).ref:refs/heads/mainneeds to remain a trusted subject at all, or whether every AWS-assuming job could bind anenvironment:instead, leaving only environment subjects in the trust policy. See the caveat in fix(iac/aws): extend OIDC trust allowlist to cover every job assuming cudly_deploy cloud-commitments-cli#1683 before narrowing further.Related
id-token: writefrom the two ungated jobs indeploy-aws-lambda.ymlVerification method
All four evidence points are reproducible read-only: